diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml
deleted file mode 100644
index 764185d9..00000000
--- a/.github/workflows/benchmarks.yml
+++ /dev/null
@@ -1,44 +0,0 @@
-name: Integration test
-
-on: [push]
-
-env:
- PYTHONIOENCODING: "utf-8"
-
-jobs:
- build:
- runs-on: t4_gpu
- steps:
- - uses: actions/checkout@v3
- - name: Set up Python 3.11
- uses: actions/setup-python@v4
- with:
- python-version: 3.11
- - name: Install python dependencies
- run: |
- pip install poetry
- poetry install
- - name: Run detection benchmark test
- run: |
- poetry run python benchmark/detection.py --max_rows 2
- poetry run python benchmark/utils/verify_benchmark_scores.py results/benchmark/det_bench/results.json --bench_type detection
- - name: Run recognition benchmark test
- run: |
- poetry run python benchmark/recognition.py --max_rows 2
- poetry run python benchmark/utils/verify_benchmark_scores.py results/benchmark/rec_bench/results.json --bench_type recognition
- - name: Run layout benchmark test
- run: |
- poetry run python benchmark/layout.py --max_rows 5
- poetry run python benchmark/utils/verify_benchmark_scores.py results/benchmark/layout_bench/results.json --bench_type layout
- - name: Run ordering benchmark
- run: |
- poetry run python benchmark/ordering.py --max_rows 5
- poetry run python benchmark/utils/verify_benchmark_scores.py results/benchmark/order_bench/results.json --bench_type ordering
- - name: Run table recognition benchmark
- run: |
- poetry run python benchmark/table_recognition.py --max_rows 5
- poetry run python benchmark/utils/verify_benchmark_scores.py results/benchmark/table_rec_bench/results.json --bench_type table_recognition
- - name: Run texify benchmark
- run: |
- poetry run python benchmark/texify.py --max_rows 5
- poetry run python benchmark/utils/verify_benchmark_scores.py results/benchmark/texify_bench/results.json --bench_type texify
\ No newline at end of file
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 34ac4343..98e6f0b0 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -8,15 +8,23 @@ jobs:
strategy:
matrix:
os: [t4_gpu, ubuntu-latest, windows-latest]
+ fail-fast: false
+ env:
+ # T4 can't run bf16 in vllm; size for the 16GB card. (No-op on the
+ # CPU runners, which skip the VLM-backed tests.)
+ VLLM_DTYPE: float16
+ VLLM_GPU_TYPE: t4
+ SURYA_INFERENCE_STARTUP_TIMEOUT: "1200"
steps:
- - uses: actions/checkout@v3
- - name: Set up Python 3.11
- uses: actions/setup-python@v4
+ - uses: actions/checkout@v4
+ - name: Install uv
+ uses: astral-sh/setup-uv@v3
with:
- python-version: 3.11
- - name: Install python dependencies
- run: |
- pip install poetry
- poetry install
+ version: latest
+ enable-cache: true
+ - name: Set up Python 3.11
+ run: uv python install 3.11
+ - name: Install dependencies
+ run: uv sync --frozen --group dev
- name: Run tests
- run: poetry run pytest
\ No newline at end of file
+ run: uv run pytest
\ No newline at end of file
diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml
index 2ffb50e8..99217a3f 100644
--- a/.github/workflows/cla.yml
+++ b/.github/workflows/cla.yml
@@ -26,7 +26,7 @@ jobs:
PERSONAL_ACCESS_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
with:
path-to-signatures: 'signatures/version1/cla.json'
- path-to-document: 'https://github.com/VikParuchuri/surya/blob/master/CLA.md'
+ path-to-document: 'https://github.com/datalab-to/surya/blob/master/CLA.md'
# branch should not be protected
branch: 'master'
allowlist: VikParuchuri
\ No newline at end of file
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index c10c70f2..defbdc74 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -7,21 +7,17 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
- - name: Set up Python 3.11
- uses: actions/setup-python@v4
+ - uses: actions/checkout@v4
+ - name: Install uv
+ uses: astral-sh/setup-uv@v3
with:
- python-version: 3.11
- - name: Install python dependencies
- run: |
- pip install poetry
- poetry install
+ version: latest
+ enable-cache: true
+ - name: Set up Python 3.11
+ run: uv python install 3.11
- name: Build package
- run: |
- poetry build
+ run: uv build
- name: Publish package
env:
- PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
- run: |
- poetry config pypi-token.pypi "$PYPI_TOKEN"
- poetry publish
+ UV_PUBLISH_TOKEN: ${{ secrets.PYPI_TOKEN }}
+ run: uv publish
diff --git a/.github/workflows/scripts.yml b/.github/workflows/scripts.yml
index b36d6c3c..500f27ae 100644
--- a/.github/workflows/scripts.yml
+++ b/.github/workflows/scripts.yml
@@ -5,33 +5,36 @@ on: [push]
jobs:
build:
runs-on: t4_gpu
+ env:
+ # T4 (Turing, compute 7.5) can't run bf16 in vllm; size vllm for the 16GB
+ # card and give the cold start (image pull + model download) headroom.
+ VLLM_DTYPE: float16
+ VLLM_GPU_TYPE: t4
+ SURYA_INFERENCE_STARTUP_TIMEOUT: "1200"
steps:
- - uses: actions/checkout@v3
- - name: Set up Python 3.11
- uses: actions/setup-python@v4
+ - uses: actions/checkout@v4
+ - name: Install uv
+ uses: astral-sh/setup-uv@v3
with:
- python-version: 3.11
- - name: Install python dependencies
- run: |
- pip install poetry
- poetry install
+ version: latest
+ enable-cache: true
+ - name: Set up Python 3.11
+ run: uv python install 3.11
+ - name: Install dependencies
+ run: uv sync --frozen --group dev
- name: Download benchmark data
run: |
wget -O benchmark_data.zip "https://drive.google.com/uc?export=download&id=1NHrdYatR1rtqs2gPVfdvO0BAvocH8CJi"
unzip -o benchmark_data.zip
- name: Test detection
- run: poetry run surya_detect benchmark_data/pdfs/switch_trans.pdf --page_range 0
+ run: uv run surya_detect benchmark_data/pdfs/switch_trans.pdf --page_range 0
+ # Spawn the vllm server once and reuse it across the OCR/layout/table
+ # steps (--keep_server) instead of paying a cold start three times.
- name: Test OCR
- env:
- RECOGNITION_MAX_TOKENS: 25
- run: poetry run surya_ocr benchmark_data/pdfs/switch_trans.pdf --page_range 0
+ run: uv run surya_ocr benchmark_data/pdfs/switch_trans.pdf --page_range 0 --keep_server
- name: Test layout
- run: poetry run surya_layout benchmark_data/pdfs/switch_trans.pdf --page_range 0
+ run: uv run surya_layout benchmark_data/pdfs/switch_trans.pdf --page_range 0 --keep_server
- name: Test table
- run: poetry run surya_table benchmark_data/pdfs/switch_trans.pdf --page_range 0
- - name: Test texify
- env:
- TEXIFY_MAX_TOKENS: 25
- run: poetry run surya_latex_ocr benchmark_data/pdfs/switch_trans.pdf --page_range 0
+ run: uv run surya_table benchmark_data/pdfs/switch_trans.pdf --page_range 0 --keep_server
- name: Test detection folder
- run: poetry run surya_detect benchmark_data/pdfs --page_range 0
+ run: uv run surya_detect benchmark_data/pdfs --page_range 0
diff --git a/CITATION.cff b/CITATION.cff
index 3837f89d..a3209d60 100644
--- a/CITATION.cff
+++ b/CITATION.cff
@@ -6,6 +6,6 @@ authors:
given-names: Vikas
- name: Datalab Team
date-released: 2025-05-13
-url: https://github.com/VikParuchuri/surya
-version: 0.14.0
-repository-code: https://github.com/VikParuchuri/surya
\ No newline at end of file
+url: https://github.com/datalab-to/surya
+version: 1.0.0
+repository-code: https://github.com/datalab-to/surya
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
index 86735afe..59711bc9 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,674 +1,51 @@
- GNU GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
- Copyright (C) 2007 Free Software Foundation, Inc.
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
- Preamble
+1. Definitions.
- The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
+"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
- The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works. By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users. We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors. You can apply it to
-your programs, too.
+"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
+"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
- To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights. Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
+"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received. You must make sure that they, too, receive
-or can get the source code. And you must show them these terms so they
-know their rights.
+"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
- Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
+"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
- For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software. For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
+"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
- Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so. This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software. The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable. Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products. If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
+"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
- Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary. To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
+"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
- The precise terms and conditions for copying, distribution and
-modification follow.
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
- TERMS AND CONDITIONS
+2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
- 0. Definitions.
+3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
- "This License" refers to version 3 of the GNU General Public License.
+4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
- "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
+You must give any other recipients of the Work or Derivative Works a copy of this License; and
+You must cause any modified files to carry prominent notices stating that You changed the files; and
+You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
+If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
+You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
- "The Program" refers to any copyrightable work licensed under this
-License. Each licensee is addressed as "you". "Licensees" and
-"recipients" may be individuals or organizations.
+5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
- To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy. The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
+6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
- A "covered work" means either the unmodified Program or a work based
-on the Program.
+7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
- To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy. Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
+8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
- To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies. Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
+9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
- An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License. If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
- 1. Source Code.
-
- The "source code" for a work means the preferred form of the work
-for making modifications to it. "Object code" means any non-source
-form of a work.
-
- A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
- The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form. A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
- The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities. However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work. For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
- The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
- The Corresponding Source for a work in source code form is that
-same work.
-
- 2. Basic Permissions.
-
- All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met. This License explicitly affirms your unlimited
-permission to run the unmodified Program. The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work. This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
- You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force. You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright. Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
- Conveying under any other circumstances is permitted solely under
-the conditions stated below. Sublicensing is not allowed; section 10
-makes it unnecessary.
-
- 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
- No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
- When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
- 4. Conveying Verbatim Copies.
-
- You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
- You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
- 5. Conveying Modified Source Versions.
-
- You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
- a) The work must carry prominent notices stating that you modified
- it, and giving a relevant date.
-
- b) The work must carry prominent notices stating that it is
- released under this License and any conditions added under section
- 7. This requirement modifies the requirement in section 4 to
- "keep intact all notices".
-
- c) You must license the entire work, as a whole, under this
- License to anyone who comes into possession of a copy. This
- License will therefore apply, along with any applicable section 7
- additional terms, to the whole of the work, and all its parts,
- regardless of how they are packaged. This License gives no
- permission to license the work in any other way, but it does not
- invalidate such permission if you have separately received it.
-
- d) If the work has interactive user interfaces, each must display
- Appropriate Legal Notices; however, if the Program has interactive
- interfaces that do not display Appropriate Legal Notices, your
- work need not make them do so.
-
- A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit. Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
- 6. Conveying Non-Source Forms.
-
- You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
- a) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by the
- Corresponding Source fixed on a durable physical medium
- customarily used for software interchange.
-
- b) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by a
- written offer, valid for at least three years and valid for as
- long as you offer spare parts or customer support for that product
- model, to give anyone who possesses the object code either (1) a
- copy of the Corresponding Source for all the software in the
- product that is covered by this License, on a durable physical
- medium customarily used for software interchange, for a price no
- more than your reasonable cost of physically performing this
- conveying of source, or (2) access to copy the
- Corresponding Source from a network server at no charge.
-
- c) Convey individual copies of the object code with a copy of the
- written offer to provide the Corresponding Source. This
- alternative is allowed only occasionally and noncommercially, and
- only if you received the object code with such an offer, in accord
- with subsection 6b.
-
- d) Convey the object code by offering access from a designated
- place (gratis or for a charge), and offer equivalent access to the
- Corresponding Source in the same way through the same place at no
- further charge. You need not require recipients to copy the
- Corresponding Source along with the object code. If the place to
- copy the object code is a network server, the Corresponding Source
- may be on a different server (operated by you or a third party)
- that supports equivalent copying facilities, provided you maintain
- clear directions next to the object code saying where to find the
- Corresponding Source. Regardless of what server hosts the
- Corresponding Source, you remain obligated to ensure that it is
- available for as long as needed to satisfy these requirements.
-
- e) Convey the object code using peer-to-peer transmission, provided
- you inform other peers where the object code and Corresponding
- Source of the work are being offered to the general public at no
- charge under subsection 6d.
-
- A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
- A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling. In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage. For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product. A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
- "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source. The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
- If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information. But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
- The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed. Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
- Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
- 7. Additional Terms.
-
- "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law. If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
- When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it. (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.) You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
- Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
- a) Disclaiming warranty or limiting liability differently from the
- terms of sections 15 and 16 of this License; or
-
- b) Requiring preservation of specified reasonable legal notices or
- author attributions in that material or in the Appropriate Legal
- Notices displayed by works containing it; or
-
- c) Prohibiting misrepresentation of the origin of that material, or
- requiring that modified versions of such material be marked in
- reasonable ways as different from the original version; or
-
- d) Limiting the use for publicity purposes of names of licensors or
- authors of the material; or
-
- e) Declining to grant rights under trademark law for use of some
- trade names, trademarks, or service marks; or
-
- f) Requiring indemnification of licensors and authors of that
- material by anyone who conveys the material (or modified versions of
- it) with contractual assumptions of liability to the recipient, for
- any liability that these contractual assumptions directly impose on
- those licensors and authors.
-
- All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10. If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term. If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
- If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
- Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
- 8. Termination.
-
- You may not propagate or modify a covered work except as expressly
-provided under this License. Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
- However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
- Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
- Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License. If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
- 9. Acceptance Not Required for Having Copies.
-
- You are not required to accept this License in order to receive or
-run a copy of the Program. Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance. However,
-nothing other than this License grants you permission to propagate or
-modify any covered work. These actions infringe copyright if you do
-not accept this License. Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
- 10. Automatic Licensing of Downstream Recipients.
-
- Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License. You are not responsible
-for enforcing compliance by third parties with this License.
-
- An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations. If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
- You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License. For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
- 11. Patents.
-
- A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based. The
-work thus licensed is called the contributor's "contributor version".
-
- A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version. For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
- Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
- In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement). To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
- If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients. "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
- If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
- A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License. You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
- Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
- 12. No Surrender of Others' Freedom.
-
- If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all. For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
- 13. Use with the GNU Affero General Public License.
-
- Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work. The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
- 14. Revised Versions of this License.
-
- The Free Software Foundation may publish revised and/or new versions of
-the GNU General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Program specifies that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation. If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
- If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
- Later license versions may give you additional or different
-permissions. However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
- 15. Disclaimer of Warranty.
-
- THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- 16. Limitation of Liability.
-
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
- 17. Interpretation of Sections 15 and 16.
-
- If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- Surya OCR
- Copyright (C) 2024 Endless Labs, Inc.
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-
-Also add information on how to contact you by electronic and paper mail.
-
- If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
- Surya OCR Copyright (C) 2024 Endless Labs, Inc.
- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
- You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
- .
-
- The GNU General Public License does not permit incorporating your program
-into proprietary programs. If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License. But first, please read
-.
\ No newline at end of file
+END OF TERMS AND CONDITIONS
\ No newline at end of file
diff --git a/MODEL_LICENSE b/MODEL_LICENSE
index cc1b8639..13b388a6 100644
--- a/MODEL_LICENSE
+++ b/MODEL_LICENSE
@@ -53,7 +53,7 @@ As conditions to the Licenses set forth in this Agreement, You agree not to use,
(a) In any way that violates any applicable national, federal, state, local or international law or regulation; or
(b) to directly or indirectly infringe or misappropriate any third party intellectual property rights (including those of Licensor or any Contributor)
2. Commercial:
-(a) for any purpose if You (your employer, or the entity you are affiliated with) generated more than two million US Dollars ($2,000,000) in gross revenue in the prior year, except where Your Use is limited to personal use or research purposes;
-(b) for any purpose if You (your employer, or the entity you are affiliated with) has raised more than two million US dollars ($2,000,000) in total equity or debt funding from any source, except where Your Use is limited to personal use or research purposes; or
+(a) for any purpose if You (your employer, or the entity you are affiliated with) generated more than two million US Dollars ($5,000,000) in gross revenue in the prior year, except where Your Use is limited to personal use or research purposes;
+(b) for any purpose if You (your employer, or the entity you are affiliated with) has raised more than two million US dollars ($5,000,000) in total equity or debt funding from any source, except where Your Use is limited to personal use or research purposes; or
(c) for any purpose if You (your employer, or the entity you are affiliated with) provides or otherwise makes available any product or service that competes with any product or service offered by or made available by Licensor or any of its affiliates.
Commercial and broader use licenses may be available from Licensor at the following URL: https://www.datalab.to/
\ No newline at end of file
diff --git a/README.md b/README.md
index 929dfcf8..5030e24c 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
State of the Art models for Document Intelligence
-
+
@@ -20,84 +20,114 @@
# Surya
-Surya is a document OCR toolkit that does:
+Surya is a 650M param OCR model with these features:
-- OCR in 90+ languages that benchmarks favorably vs cloud services
-- Line-level text detection in any language
-- Layout analysis (table, image, header, etc detection)
-- Reading order detection
-- Table recognition (detecting rows/columns)
-- LaTeX OCR
+- Accuracy - scores 83.3% on [olmOCR-bench](https://huggingface.co/datasets/allenai/olmOCR-bench) (top under 3B params)
+- Speed - throughput of 5 pages/s on an RTX 5090
+- Multilingual - scores 87.2% on an internal benchmark set of 91 languages (more [here](#multilingual))
+- Layout analysis (table, image, header, etc.) with reading order
+- Table recognition (rows + columns)
-It works on a range of documents (see [usage](#usage) and [benchmarks](#benchmarks) for more details).
+We also ship smaller models for line-level text detection and ocr error detection. It works on a range of documents (see [usage](#usage) and [benchmarks](#benchmarks)).
## Try Datalab's Managed Platform
-Our managed platform runs our latest open source model, [Chandra](https://github.com/datalab-to/chandra) — higher accuracy than Surya, with zero data retention by default, SOC 2 Type 2, and custom BAAs.
+Our managed platform runs both Surya, and variants of our highest accuracy model, [Chandra](https://github.com/datalab-to/chandra).
-If you have high volume workloads, we offer a batch processing service that has processed 200M+ pages per week — we manage the infrastructure so your workloads finish on time.
+Get started with **$5 in free credits** — [sign up](https://www.datalab.to/?utm_source=gh-surya) (takes under 30 seconds) or try our free [public playground](https://www.datalab.to/playground?utm_source=gh-surya).
-Get started with **$5 in free credits** — [sign up](https://www.datalab.to/?utm_source=gh-surya) (takes under 30 seconds) or try our [public playground](https://www.datalab.to/playground?utm_source=gh-surya).
+## Model Information
-Commercial self-hosting requires a license — see [Commercial usage](#commercial-usage). For on-prem licensing, [contact us](https://www.datalab.to/contact?utm_source=gh-surya-onprem).
+
| Detection | OCR |
|:----------------------------------------------------------------:|:-----------------------------------------------------------------------:|
-| | |
+| | |
-| Layout | Reading Order |
-|:------------------------------------------------------------------:|:--------------------------------------------------------------------------:|
-| | |
-
-| Table Recognition | LaTeX OCR |
-|:-------------------------------------------------------------:|:------------------------------------------------------:|
-| | |
+| Layout | Table Recognition |
+|:------------------------------------------------------------------:|:-------------------------------------------------------------:|
+| | |
Surya is named for the [Hindu sun god](https://en.wikipedia.org/wiki/Surya), who has universal vision.
-## Community
-
-[Discord](https://discord.gg//KuZwXNGnfH) is where we discuss future development.
-
## Examples
-| Name | Detection | OCR | Layout | Order | Table Rec |
-|------------------|:-----------------------------------:|-----------------------------------------:|-------------------------------------------:|--------------------------------------------:|---------------------------------------------:|
-| Japanese | [Image](static/images/japanese.jpg) | [Image](static/images/japanese_text.jpg) | [Image](static/images/japanese_layout.jpg) | [Image](static/images/japanese_reading.jpg) | [Image](static/images/japanese_tablerec.png) |
-| Chinese | [Image](static/images/chinese.jpg) | [Image](static/images/chinese_text.jpg) | [Image](static/images/chinese_layout.jpg) | [Image](static/images/chinese_reading.jpg) | |
-| Hindi | [Image](static/images/hindi.jpg) | [Image](static/images/hindi_text.jpg) | [Image](static/images/hindi_layout.jpg) | [Image](static/images/hindi_reading.jpg) | |
-| Arabic | [Image](static/images/arabic.jpg) | [Image](static/images/arabic_text.jpg) | [Image](static/images/arabic_layout.jpg) | [Image](static/images/arabic_reading.jpg) | |
-| Chinese + Hindi | [Image](static/images/chi_hind.jpg) | [Image](static/images/chi_hind_text.jpg) | [Image](static/images/chi_hind_layout.jpg) | [Image](static/images/chi_hind_reading.jpg) | |
-| Presentation | [Image](static/images/pres.png) | [Image](static/images/pres_text.jpg) | [Image](static/images/pres_layout.jpg) | [Image](static/images/pres_reading.jpg) | [Image](static/images/pres_tablerec.png) |
-| Scientific Paper | [Image](static/images/paper.jpg) | [Image](static/images/paper_text.jpg) | [Image](static/images/paper_layout.jpg) | [Image](static/images/paper_reading.jpg) | [Image](static/images/paper_tablerec.png) |
-| Scanned Document | [Image](static/images/scanned.png) | [Image](static/images/scanned_text.jpg) | [Image](static/images/scanned_layout.jpg) | [Image](static/images/scanned_reading.jpg) | [Image](static/images/scanned_tablerec.png) |
-| New York Times | [Image](static/images/nyt.jpg) | [Image](static/images/nyt_text.jpg) | [Image](static/images/nyt_layout.jpg) | [Image](static/images/nyt_order.jpg) | |
-| Scanned Form | [Image](static/images/funsd.png) | [Image](static/images/funsd_text.jpg) | [Image](static/images/funsd_layout.jpg) | [Image](static/images/funsd_reading.jpg) | [Image](static/images/scanned_tablerec2.png) |
-| Textbook | [Image](static/images/textbook.jpg) | [Image](static/images/textbook_text.jpg) | [Image](static/images/textbook_layout.jpg) | [Image](static/images/textbook_order.jpg) | |
+Each row links to five annotated views of the same page: text-line detection, OCR, layout, reading order, and (when present) table recognition.
-# Commercial usage
+| Name | Detection | OCR | Layout | Order | Table Rec |
+|-------------------|:-----------------------------------:|------------------------------------------:|---------------------------------------------:|------------------------------------------------:|------------------------------------------------:|
+| Newspaper | [Image](static/images/newspaper.png) | [Image](static/images/newspaper_text.png) | [Image](static/images/newspaper_layout.png) | [Image](static/images/newspaper_reading.png) | |
+| Textbook | [Image](static/images/textbook.png) | [Image](static/images/textbook_text.png) | [Image](static/images/textbook_layout.png) | [Image](static/images/textbook_reading.png) | |
+| Tax Form | [Image](static/images/form.png) | [Image](static/images/form_text.png) | [Image](static/images/form_layout.png) | [Image](static/images/form_reading.png) | [Image](static/images/form_tablerec.png) |
+| Handwritten Notes | [Image](static/images/handwritten.png) | [Image](static/images/handwritten_text.png) | [Image](static/images/handwritten_layout.png) | [Image](static/images/handwritten_reading.png) | [Image](static/images/handwritten_tablerec.png) |
+| Corporate Doc | [Image](static/images/corporate.png) | [Image](static/images/corporate_text.png) | [Image](static/images/corporate_layout.png) | [Image](static/images/corporate_reading.png) | [Image](static/images/corporate_tablerec.png) |
-Our model weights use a modified AI Pubs Open Rail-M license (free for research, personal use, and startups under $2M funding/revenue) and our code is GPL. For broader commercial licensing or to remove GPL requirements, visit our pricing page [here](https://www.datalab.to/pricing?utm_source=gh-surya).
+# Commercial usage
+The Surya code is licensed under Apache 2.0. The model weights use a modified AI Pubs Open Rail-M license (free for research, personal use, and startups under $5M funding/revenue). For broader commercial licensing of the model weights, visit our pricing page [here](https://www.datalab.to/pricing?utm_source=gh-surya).
# Installation
-You'll need python 3.10+ and PyTorch. You may need to install the CPU version of torch first if you're not using a Mac or a GPU machine. See [here](https://pytorch.org/get-started/locally/) for more details.
-
Install with:
```shell
pip install surya-ocr
```
-Model weights will automatically download the first time you run surya.
+## Inference backend prerequisites
+
+Surya auto-spawns the server on first use, and you need `vllm` (NVIDIA GPU) or `llama.cpp` (CPU / Apple Silicon):
+
+- **NVIDIA GPU:** [Docker](https://docs.docker.com/get-docker/) plus the [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html).
+- **CPU / Apple Silicon:** the `llama-server` binary from llama.cpp:
+ ```shell
+ brew install llama.cpp # macOS
+ # or grab a release from https://github.com/ggml-org/llama.cpp/releases
+ ```
+
+## Upgrading from Surya v1
+
+If you have v1 code, you can migrate to this:
+
+```python
+# v2
+from surya.inference import SuryaInferenceManager
+from surya.recognition import RecognitionPredictor
+
+manager = SuryaInferenceManager() # auto-spawns vllm or llama-server
+rec = RecognitionPredictor(manager)
+predictions = rec([image])
+```
+
+What's different:
+- `SuryaInferenceManager` replaces `FoundationPredictor`. Same manager instance is shared across `LayoutPredictor`, `RecognitionPredictor`, `TableRecPredictor`.
+- Output schemas changed: see the per-section JSON tables below. Highlights — `text_lines` → `blocks` (with `html`); layout dropped `top_k`, added `count`; table_rec dropped `is_header` / `colspan` / `rowspan` from cells.
# Usage
-- Inspect the settings in `surya/settings.py`. You can override any settings with environment variables.
-- Your torch device will be automatically detected, but you can override this. For example, `TORCH_DEVICE=cuda`.
+Surya 2 runs layout, OCR, and table recognition through a single VLM. The inference manager will spawn one for you on first use; you can also point it at an existing server via `SURYA_INFERENCE_URL=http://host:port/v1`.
+
+- Inspect the settings in `surya/settings.py`. You can override any setting via env var (e.g. `SURYA_INFERENCE_BACKEND=vllm`).
+- Text detection and OCR errors are separate models.
+
+### Server lifecycle (`--keep_server`)
+
+By default each command spawns the VLM server on startup and shuts it down on
+exit — so running several commands in a row pays the startup (and, on GPU, the
+model-load) cost every time. Pass `--keep_server` to leave the server running
+so later commands attach to it instead of re-spawning:
+
+```shell
+surya_ocr DATA_PATH --keep_server # spawns the server and leaves it up
+surya_layout DATA_PATH # attaches to the running server
+surya_table DATA_PATH # ...and so on, no re-spawn
+```
+
+`--keep_server` works on every command. Stop the server when you're done
+(`docker stop` the `surya-vllm-*` container, or kill the `llama-server`
+process), or set `SURYA_INFERENCE_KEEP_ALIVE=1` to make keep-alive the default.
## Interactive App
@@ -117,52 +147,52 @@ surya_ocr DATA_PATH
```
- `DATA_PATH` can be an image, pdf, or folder of images/pdfs
-- `--task_name` will specify which task to use for predicting the lines. `ocr_with_boxes` is the default, which will format text and give you bboxes. If you get bad performance, try `ocr_without_boxes`, which will give you potentially better performance but no bboxes. For blocks like equations and paragraphs, try `block_without_boxes`.
-- `--images` will save images of the pages and detected text lines (optional)
+- `--images` will save images of the pages and detected blocks (optional)
- `--output_dir` specifies the directory to save results to instead of the default
- `--page_range` specifies the page range to process in the PDF, specified as a single number, a comma separated list, a range, or comma separated ranges - example: `0,5-10,20`.
-- `--disable_math` - by default, surya will recognize math in text. This can lead to false positives - you can disable this with this flag.
-
-The `results.json` file will contain a json dictionary where the keys are the input filenames without extensions. Each value will be a list of dictionaries, one per page of the input document. Each page dictionary contains:
-
-- `text_lines` - the detected text and bounding boxes for each line
- - `text` - the text in the line
- - `confidence` - the confidence of the model in the detected text (0-1)
- - `polygon` - the polygon for the text line in (x1, y1), (x2, y2), (x3, y3), (x4, y4) format. The points are in clockwise order from the top left.
- - `bbox` - the axis-aligned rectangle for the text line in (x1, y1, x2, y2) format. (x1, y1) is the top left corner, and (x2, y2) is the bottom right corner.
- - `chars` - the individual characters in the line
- - `text` - the text of the character
- - `bbox` - the character bbox (same format as line bbox)
- - `polygon` - the character polygon (same format as line polygon)
- - `confidence` - the confidence of the model in the detected character (0-1)
- - `bbox_valid` - if the character is a special token or math, the bbox may not be valid
- - `words` - the individual words in the line (computed from the characters)
- - `text` - the text of the word
- - `bbox` - the word bbox (same format as line bbox)
- - `polygon` - the word polygon (same format as line polygon)
- - `confidence` - mean character confidence
- - `bbox_valid` - if the word is a special token or math, the bbox may not be valid
-- `page` - the page number in the file
-- `image_bbox` - the bbox for the image in (x1, y1, x2, y2) format. (x1, y1) is the top left corner, and (x2, y2) is the bottom right corner. All line bboxes will be contained within this bbox.
+- `--keep_server` leaves the inference server running after the command exits so later commands reuse it (see [Server lifecycle](#server-lifecycle---keep_server)). Available on every command.
+
+The `results.json` file contains a dict keyed by input filename (no extension). Each value is a list of page dicts. Each page dict contains:
+
+- `blocks` - per-block OCR results in reading order
+ - `label` - canonicalized layout label (e.g. `Text`, `SectionHeader`, `Table`, `Equation`, `Picture`, `Form`, `PageHeader`, ...). See `surya/layout/label.py:LAYOUT_PRED_RELABEL` for the full canonical-name set.
+ - `raw_label` - original label emitted by the model, before canonicalization
+ - `reading_order` - 0-indexed position in layout output
+ - `html` - block content as HTML (math wrapped in `... `, tables as ``, etc.). `""` if the block was skipped
+ - `polygon` - 4-corner polygon in `[[x0,y0],[x1,y0],[x1,y1],[x0,y1]]` order
+ - `bbox` - axis-aligned `[x0, y0, x1, y1]` derived from the polygon
+ - `confidence` - mean per-token probability across the block's decode (0-1)
+ - `skipped` - true if the block was a visual label (e.g. Picture) and not OCR'd
+ - `error` - true if the block OCR call failed
+- `image_bbox` - `[0, 0, width, height]` for the page image
**Performance tips**
-Setting the `RECOGNITION_BATCH_SIZE` env var properly will make a big difference when using a GPU. Each batch item will use `40MB` of VRAM, so very high batch sizes are possible. The default is a batch size `512`, which will use about 20GB of VRAM. Depending on your CPU core count, it may help, too - the default CPU batch size is `32`.
+- Throughput is governed by the inference backend. With `vllm`, raise `--max-num-seqs` / `--max-num-batched-tokens` (or `SURYA_INFERENCE_PARALLEL` on the client side) to keep more pages in flight. With `llama.cpp`, set `SURYA_INFERENCE_PARALLEL` to match `--parallel` on `llama-server`.
+- DPI can also impact throughput significantly - you can adjust the DPI settings to make the right throughput/accuracy tradeoff for your usecase. Try going from 192 to 96 for improved throughput.
+- MTP can also impact latency/throughput - you can adjust the vllm mtp config in settings.
### From python
```python
from PIL import Image
-from surya.foundation import FoundationPredictor
+from surya.inference import SuryaInferenceManager
from surya.recognition import RecognitionPredictor
-from surya.detection import DetectionPredictor
-image = Image.open(IMAGE_PATH)
-foundation_predictor = FoundationPredictor()
-recognition_predictor = RecognitionPredictor(foundation_predictor)
-detection_predictor = DetectionPredictor()
+manager = SuryaInferenceManager()
+recognition_predictor = RecognitionPredictor(manager)
+
+# Default: full-page OCR. One VLM call per page. Returns one PageOCRResult per
+# image: `.blocks` (each with label, html, polygon, bbox, confidence, ...) and
+# `.image_bbox` — the same schema as block mode.
+predictions = recognition_predictor([Image.open(IMAGE_PATH)])
-predictions = recognition_predictor([image], det_predictor=detection_predictor)
+# Block mode: pre-run layout, then per-block OCR. Same return schema as above.
+# Auto-selected when `layout_results` is passed.
+from surya.layout import LayoutPredictor
+layout = LayoutPredictor(manager)
+layouts = layout([Image.open(IMAGE_PATH)])
+predictions = recognition_predictor([Image.open(IMAGE_PATH)], layouts)
```
@@ -192,7 +222,7 @@ The `results.json` file will contain a json dictionary where the keys are the in
**Performance tips**
-Setting the `DETECTOR_BATCH_SIZE` env var properly will make a big difference when using a GPU. Each batch item will use `440MB` of VRAM, so very high batch sizes are possible. The default is a batch size `36`, which will use about 16GB of VRAM. Depending on your CPU core count, it might help, too - the default CPU batch size is `6`.
+Detection is a torch model. `DETECTOR_BATCH_SIZE` defaults to an auto-picked value at runtime; override the env var to control VRAM usage on GPU and raise it on larger cards.
### From python
@@ -200,11 +230,8 @@ Setting the `DETECTOR_BATCH_SIZE` env var properly will make a big difference wh
from PIL import Image
from surya.detection import DetectionPredictor
-image = Image.open(IMAGE_PATH)
det_predictor = DetectionPredictor()
-
-# predictions is a list of dicts, one per image
-predictions = det_predictor([image])
+predictions = det_predictor([Image.open(IMAGE_PATH)])
```
## Layout and reading order
@@ -220,148 +247,114 @@ surya_layout DATA_PATH
- `--output_dir` specifies the directory to save results to instead of the default
- `--page_range` specifies the page range to process in the PDF, specified as a single number, a comma separated list, a range, or comma separated ranges - example: `0,5-10,20`.
-The `results.json` file will contain a json dictionary where the keys are the input filenames without extensions. Each value will be a list of dictionaries, one per page of the input document. Each page dictionary contains:
+The `results.json` file contains a dict keyed by input filename (no extension). Each value is a list of page dicts. Each page dict contains:
-- `bboxes` - detected bounding boxes for text
- - `bbox` - the axis-aligned rectangle for the text line in (x1, y1, x2, y2) format. (x1, y1) is the top left corner, and (x2, y2) is the bottom right corner.
- - `polygon` - the polygon for the text line in (x1, y1), (x2, y2), (x3, y3), (x4, y4) format. The points are in clockwise order from the top left.
- - `position` - the reading order of the box.
- - `label` - the label for the bbox. One of `Caption`, `Footnote`, `Formula`, `List-item`, `Page-footer`, `Page-header`, `Picture`, `Figure`, `Section-header`, `Table`, `Form`, `Table-of-contents`, `Handwriting`, `Text`, `Text-inline-math`.
- - `top_k` - the top-k other potential labels for the box. A dictionary with labels as keys and confidences as values.
-- `page` - the page number in the file
-- `image_bbox` - the bbox for the image in (x1, y1, x2, y2) format. (x1, y1) is the top left corner, and (x2, y2) is the bottom right corner. All line bboxes will be contained within this bbox.
+- `bboxes` - layout boxes in reading order
+ - `polygon` - 4-corner polygon `[[x0,y0],[x1,y0],[x1,y1],[x0,y1]]`
+ - `bbox` - axis-aligned `[x0, y0, x1, y1]` derived from the polygon
+ - `label` - canonicalized label. One of `Caption`, `Footnote`, `Equation`, `ListGroup`, `PageHeader`, `PageFooter`, `Picture`, `SectionHeader`, `Table`, `Text`, `Figure`, `Code`, `Form`, `TableOfContents`, `ChemicalBlock`, `Diagram`, `Bibliography`, `BlankPage`
+ - `raw_label` - original label emitted by the model
+ - `position` - 0-indexed reading order
+ - `count` - model's token estimate for OCR'ing this block (rounded to multiples of 50; used to size the per-block decode budget)
+ - `confidence` - mean per-token probability across the layout decode (0-1)
+- `image_bbox` - `[0, 0, width, height]`
+- `raw` - raw JSON the layout model emitted, for debugging
+- `error` - true if the layout call failed
**Performance tips**
-Setting the `LAYOUT_BATCH_SIZE` env var properly will make a big difference when using a GPU. Each batch item will use `220MB` of VRAM, so very high batch sizes are possible. The default is a batch size `32`, which will use about 7GB of VRAM. Depending on your CPU core count, it might help, too - the default CPU batch size is `4`.
+Layout runs through the shared inference backend. Throughput tuning is the same as OCR — see Performance tips above.
### From python
```python
from PIL import Image
-from surya.foundation import FoundationPredictor
+from surya.inference import SuryaInferenceManager
from surya.layout import LayoutPredictor
-from surya.settings import settings
-
-image = Image.open(IMAGE_PATH)
-layout_predictor = LayoutPredictor(FoundationPredictor(checkpoint=settings.LAYOUT_MODEL_CHECKPOINT))
-# layout_predictions is a list of dicts, one per image
-layout_predictions = layout_predictor([image])
+layout_predictor = LayoutPredictor(SuryaInferenceManager())
+layout_predictions = layout_predictor([Image.open(IMAGE_PATH)])
```
## Table Recognition
-This command will write out a json file with the detected table cells and row/column ids, along with row/column bounding boxes. If you want to get cell positions and text, along with nice formatting, check out the [marker](https://www.github.com/VikParuchuri/marker) repo. You can use the `TableConverter` to detect and extract tables in images and PDFs. It supports output in json (with bboxes), markdown, and html.
+This command will write out a json file with the detected table cells and row/column ids, along with row/column bounding boxes. If you want to get cell positions and text, along with nice formatting, check out the [marker](https://github.com/datalab-to/marker) repo. You can use the `TableConverter` to detect and extract tables in images and PDFs. It supports output in json (with bboxes), markdown, and html.
```shell
surya_table DATA_PATH
```
- `DATA_PATH` can be an image, pdf, or folder of images/pdfs
-- `--images` will save images of the pages and detected table cells + rows and columns (optional)
+- `--images` will save annotated row + column overlays alongside the json (optional)
- `--output_dir` specifies the directory to save results to instead of the default
- `--page_range` specifies the page range to process in the PDF, specified as a single number, a comma separated list, a range, or comma separated ranges - example: `0,5-10,20`.
-- `--detect_boxes` specifies if cells should be detected. By default, they're pulled out of the PDF, but this is not always possible.
- `--skip_table_detection` tells table recognition not to detect tables first. Use this if your image is already cropped to a table.
-The `results.json` file will contain a json dictionary where the keys are the input filenames without extensions. Each value will be a list of dictionaries, one per page of the input document. Each page dictionary contains:
+The `results.json` file contains a dict keyed by input filename (no extension). Each value is a list of per-table dicts. Each table dict contains:
-- `rows` - detected table rows
- - `bbox` - the bounding box of the table row
- - `row_id` - the id of the row
- - `is_header` - if it is a header row.
+- `rows` - detected table rows in reading order
+ - `polygon` / `bbox` - row geometry (same convention as everywhere else)
+ - `row_id` - 0-indexed row id
- `cols` - detected table columns
- - `bbox` - the bounding box of the table column
- - `col_id`- the id of the column
- - `is_header` - if it is a header column
-- `cells` - detected table cells
- - `bbox` - the axis-aligned rectangle for the text line in (x1, y1, x2, y2) format. (x1, y1) is the top left corner, and (x2, y2) is the bottom right corner.
- - `text` - if text could be pulled out of the pdf, the text of this cell.
- - `row_id` - the id of the row the cell belongs to.
- - `col_id` - the id of the column the cell belongs to.
- - `colspan` - the number of columns spanned by the cell.
- - `rowspan` - the number of rows spanned by the cell.
- - `is_header` - whether it is a header cell.
-- `page` - the page number in the file
-- `table_idx` - the index of the table on the page (sorted in vertical order)
-- `image_bbox` - the bbox for the image in (x1, y1, x2, y2) format. (x1, y1) is the top left corner, and (x2, y2) is the bottom right corner. All line bboxes will be contained within this bbox.
+ - `polygon` / `bbox` - column geometry
+ - `col_id` - 0-indexed column id
+- `cells` - geometric row × column intersections (simple mode)
+ - `polygon` / `bbox` - cell geometry
+ - `row_id`, `col_id`, `cell_id`
+- `html` - full `` HTML (only populated when `predict_full` is used; handles spanning cells / header rows). `null` in simple mode.
+- `mode` - `"simple"` or `"full"`
+- `image_bbox` - the table crop bbox
+- `error` - true if the table_rec call failed
+- `raw` - raw model output, for debugging
**Performance tips**
-Setting the `TABLE_REC_BATCH_SIZE` env var properly will make a big difference when using a GPU. Each batch item will use `150MB` of VRAM, so very high batch sizes are possible. The default is a batch size `64`, which will use about 10GB of VRAM. Depending on your CPU core count, it might help, too - the default CPU batch size is `8`.
+Table recognition routes through the shared VLM. Throughput tuning is the same as OCR.
### From python
```python
from PIL import Image
+from surya.inference import SuryaInferenceManager
from surya.table_rec import TableRecPredictor
-image = Image.open(IMAGE_PATH)
-table_rec_predictor = TableRecPredictor()
+table_rec_predictor = TableRecPredictor(SuryaInferenceManager())
-table_predictions = table_rec_predictor([image])
-```
-
-## LaTeX OCR
+# Default: rows + columns only, cells derived from intersections.
+table_predictions = table_rec_predictor([Image.open(IMAGE_PATH)])
-This command will write out a json file with the LaTeX of the equations. You must pass in images that are already cropped to the equations. You can do this by running the layout model, then cropping, if you want.
-
-```shell
-surya_latex_ocr DATA_PATH
+# Or full HTML output (better for spanning cells / headers):
+# table_predictions = table_rec_predictor.predict_full([image])
```
-- `DATA_PATH` can be an image, pdf, or folder of images/pdfs
-- `--output_dir` specifies the directory to save results to instead of the default
-- `--page_range` specifies the page range to process in the PDF, specified as a single number, a comma separated list, a range, or comma separated ranges - example: `0,5-10,20`.
+## Math / equations
-The `results.json` file will contain a json dictionary where the keys are the input filenames without extensions. Each value will be a list of dictionaries, one per page of the input document. See the OCR section above for the format of the output.
+Surya 2 handles math inline as part of full-page OCR — recognized equations
+come back inside `... ` tags in the same HTML output as
+surrounding prose, in KaTeX-compatible LaTeX. No separate LaTeX OCR pass.
-### From python
+# Inference Backends
-```python
-from PIL import Image
-from surya.texify import TexifyPredictor
-
-image = Image.open(IMAGE_PATH)
-predictor = TexifyPredictor()
-
-predictor([image])
-```
+Layout / OCR / table_rec all share one VLM, served either by `vllm` (GPU) or `llama.cpp` (CPU / Apple Silicon). The `SuryaInferenceManager` will spawn one automatically; you can also point at a pre-running server:
-### Interactive app
-
-You can also run a special interactive app that lets you select equations and OCR them (kind of like MathPix snip) with:
-
-```shell
-pip install streamlit==1.40 streamlit-drawable-canvas-jsretry
-texify_gui
+```bash
+# Attach to an existing vllm
+export SURYA_INFERENCE_BACKEND=vllm
+export SURYA_INFERENCE_URL=http://localhost:8000/v1
```
-## Compilation
-
-The following models have support for compilation. You will need to set the following environment variables to enable compilation:
-
-- Detection: `COMPILE_DETECTOR=true`
-- Layout: `COMPILE_LAYOUT=true`
-- Table recognition: `COMPILE_TABLE_REC=true`
-
-Alternatively, you can also set `COMPILE_ALL=true` which will compile all models.
-
-Here are the speedups on an A10 GPU:
-
-| Model | Time per page (s) | Compiled time per page (s) | Speedup (%) |
-| ----------------- | ----------------- | -------------------------- | ----------- |
-| Detection | 0.108808 | 0.10521 | 3.306742151 |
-| Layout | 0.27319 | 0.27063 | 0.93707676 |
-| Table recognition | 0.0219 | 0.01938 | 11.50684932 |
+| Setting | Default | Notes |
+|-----------------------------------|-----------------------------------|--------------------------------------------------------|
+| `SURYA_INFERENCE_BACKEND` | auto (vllm if NVIDIA, else llamacpp) | `vllm` \| `llamacpp` \| unset (auto) |
+| `SURYA_INFERENCE_URL` | (auto-spawn) | Attach to a running OpenAI-compatible server |
+| `SURYA_INFERENCE_PARALLEL` | 8 | Client-side concurrency to the backend |
+| `SURYA_INFERENCE_KEEP_ALIVE` | false | Leave the spawned server up after exit (cf. `--keep_server`) |
+| `SURYA_GUIDED_LAYOUT` | true | JSON-schema-constrained layout decode |
# Limitations
-- This is specialized for document OCR. It will likely not work on photos or other images.
-- It is for printed text, not handwriting (though it may work on some handwriting).
-- The text detection model has trained itself to ignore advertisements.
-- You can find language support for OCR in `surya/recognition/languages.py`. Text detection, layout analysis, and reading order will work with any language.
+- This is specialized for document OCR. Performance on photos or natural scenes is not the goal.
+- Layout / OCR / table_rec all need a running inference backend (vllm or llama.cpp). Detection runs purely on torch and works without it.
## Troubleshooting
@@ -373,233 +366,125 @@ If OCR isn't working properly:
# Manual install
-If you want to develop surya, you can install it manually:
+If you want to develop surya, you can install it manually with [uv](https://docs.astral.sh/uv/):
-- `git clone https://github.com/VikParuchuri/surya.git`
-- `cd surya`
-- `poetry install` - installs main and dev dependencies
-- `poetry shell` - activates the virtual environment
+```bash
+git clone https://github.com/datalab-to/surya.git
+cd surya
+uv sync --group dev # installs runtime + dev deps
+uv run surya_ocr ... # or `source .venv/bin/activate` to enter the venv
+```
# Benchmarks
-## OCR
-
-
-
-| Model | Time per page (s) | Avg similarity (⬆) |
-|-----------|-------------------|--------------------|
-| surya | .62 | 0.97 |
-| tesseract | .45 | 0.88 |
-
-[Full language results](static/images/rec_acc_table.png)
-
-Tesseract is CPU-based, and surya is CPU or GPU. I tried to cost-match the resources used, so I used a 1xA6000 (48GB VRAM) for surya, and 28 CPU cores for Tesseract (same price on Lambda Labs/DigitalOcean).
-
-### Google Cloud Vision
-
-I benchmarked OCR against Google Cloud vision since it has similar language coverage to Surya.
-
-
-
-[Full language results](static/images/gcloud_full_langs.png)
-
-**Methodology**
-
-I measured normalized sentence similarity (0-1, higher is better) based on a set of real-world and synthetic pdfs. I sampled PDFs from common crawl, then filtered out the ones with bad OCR. I couldn't find PDFs for some languages, so I also generated simple synthetic PDFs for those.
-
-I used the reference line bboxes from the PDFs with both tesseract and surya, to just evaluate the OCR quality.
-
-For Google Cloud, I aligned the output from Google Cloud with the ground truth. I had to skip RTL languages since they didn't align well.
-
-## Text line detection
-
-
-
-| Model | Time (s) | Time per page (s) | precision | recall |
-|-----------|------------|---------------------|-------------|----------|
-| surya | 47.2285 | 0.094452 | 0.835857 | 0.960807 |
-| tesseract | 74.4546 | 0.290838 | 0.631498 | 0.997694 |
-
-
-Tesseract is CPU-based, and surya is CPU or GPU. I ran the benchmarks on a system with an A10 GPU, and a 32 core CPU. This was the resource usage:
-
-- tesseract - 32 CPU cores, or 8 workers using 4 cores each
-- surya - 36 batch size, for 16GB VRAM usage
+Surya 2 is a single VLM that handles layout analysis, OCR (full-page or
+per-block), and table recognition in one model. We evaluate end-to-end on
+[olmOCR-bench](https://huggingface.co/datasets/allenai/olmOCR-bench) — the
+standard quality benchmark for document parsers.
-**Methodology**
+## olmOCR-bench
-Surya predicts line-level bboxes, while tesseract and others predict word-level or character-level. It's hard to find 100% correct datasets with line-level annotations. Merging bboxes can be noisy, so I chose not to use IoU as the metric for evaluation.
+Pareto-optimal on the size-vs-score frontier, and best in class under 3B params.
-I instead used coverage, which calculates:
+| Model | Params | Score |
+|-----------------------------|----------:|---------:|
+| Infinity-Parser2-Pro | 35.1B | 87.6 |
+| Chandra OCR 2 (Datalab) | 5.3B | 85.9 |
+| dots.mocr | 3.0B | 83.9 |
+| **Surya OCR 2** (Datalab) | **0.65B** | **83.3** |
+| LightOnOCR 2-1B \* | 1.0B | 83.2 |
+| Chandra OCR 1 (Datalab) | 9.0B | 83.1 |
+| olmOCR (anchored) | 8.3B | 77.4 |
+| GOT OCR | 0.6B | 48.3 |
-- Precision - how well the predicted bboxes cover ground truth bboxes
-- Recall - how well ground truth bboxes cover predicted bboxes
+\* **LightOnOCR 2-1B** uses a different benchmark methodology than the other entries (see their [release notes](https://huggingface.co/lightonai/LightOnOCR-2-1B)); the score is included for context but is not directly comparable.
-First calculate coverage for each bbox, then add a small penalty for double coverage, since we want the detection to have non-overlapping bboxes. Anything with a coverage of 0.5 or higher is considered a match.
+Comparison scores from the [olmOCR-bench dataset card](https://huggingface.co/datasets/allenai/olmOCR-bench).
-Then we calculate precision and recall for the whole dataset.
+Surya 2, per-source pass rate on the `default` preset (8,413 tests total):
-## Layout analysis
+| ArXiv | Base | Hdr/Ftr | TinyTxt | MultCol | OldScan | OldMath | Tables |
+|------:|-----:|--------:|--------:|--------:|--------:|--------:|-------:|
+| 88.3 | 99.7 | 92.5 | 93.7 | 82.4 | 41.8 | 81.4 | 86.6 |
-| Layout Type | precision | recall |
-|---------------|-------------|----------|
-| Image | 0.91265 | 0.93976 |
-| List | 0.80849 | 0.86792 |
-| Table | 0.84957 | 0.96104 |
-| Text | 0.93019 | 0.94571 |
-| Title | 0.92102 | 0.95404 |
+## Multilingual
-Time per image - .13 seconds on GPU (A10).
+We also evaluate Surya 2 against a 91-language internal benchmark covering
+text accuracy, layout, tables, math, and reading order in documents drawn
+from each language.
-**Methodology**
+**Overall pass rate: 87.2% across 91 languages.** 38 of the
+91 languages score ≥ 90%; 76 score ≥ 80%.
-I benchmarked the layout analysis on [Publaynet](https://github.com/ibm-aur-nlp/PubLayNet), which was not in the training data. I had to align publaynet labels with the surya layout labels. I was then able to find coverage for each layout type:
+Top 15 widely-spoken languages:
-- Precision - how well the predicted bboxes cover ground truth bboxes
-- Recall - how well ground truth bboxes cover predicted bboxes
+| Code | Language | Score |
+|------|-------------|------:|
+| `ar` | Arabic | 72.7% |
+| `bn` | Bengali | 82.7% |
+| `zh` | Chinese | 82.5% |
+| `en` | English | 92.3% |
+| `fr` | French | 89.3% |
+| `de` | German | 89.7% |
+| `hi` | Hindi | 82.2% |
+| `it` | Italian | 93.0% |
+| `ja` | Japanese | 86.2% |
+| `ko` | Korean | 86.7% |
+| `fa` | Persian | 82.3% |
+| `pt` | Portuguese | 86.1% |
+| `ru` | Russian | 88.8% |
+| `es` | Spanish | 90.7% |
+| `vi` | Vietnamese | 73.2% |
-## Reading Order
+See [static/docs/multilingual.md](static/docs/multilingual.md) for the full 91-language table.
-88% mean accuracy, and .4 seconds per image on an A10 GPU. See methodology for notes - this benchmark is not perfect measure of accuracy, and is more useful as a sanity check.
+## Throughput
-**Methodology**
-
-I benchmarked the reading order on the layout dataset from [here](https://www.icst.pku.edu.cn/cpdp/sjzy/), which was not in the training data. Unfortunately, this dataset is fairly noisy, and not all the labels are correct. It was very hard to find a dataset annotated with reading order and also layout information. I wanted to avoid using a cloud service for the ground truth.
-
-The accuracy is computed by finding if each pair of layout boxes is in the correct order, then taking the % that are correct.
-
-## Table Recognition
+Full-page OCR, 96 DPI input (~2,400 output tokens/page average), measured
+client-side against a running inference server.
-| Model | Row Intersection | Col Intersection | Time Per Image |
-|-------------------|--------------------|--------------------|------------------|
-| Surya | 1 | 0.98625 | 0.30202 |
-| Table transformer | 0.84 | 0.86857 | 0.08082 |
+### RTX 5090 (vllm)
-Higher is better for intersection, which the percentage of the actual row/column overlapped by the predictions. This benchmark is mostly a sanity check - there is a more rigorous one in [marker](https://www.github.com/VikParuchuri/marker)
+`vllm/vllm-openai:v0.20.1`, single RTX 5090 (32 GB).
-**Methodology**
+| Concurrency | Pages/s | Tokens/s | p50 (ms) | p95 (ms) | avg tok/page |
+|------------:|--------:|----------:|---:|---:|---:|
+| 128 | 5.35 | 12,884 | 18,915 | 42,538 | 2,410 |
-The benchmark uses a subset of [Fintabnet](https://developer.ibm.com/exchanges/data/all/fintabnet/) from IBM. It has labeled rows and columns. After table recognition is run, the predicted rows and columns are compared to the ground truth. There is an additional penalty for predicting too many or too few rows/columns.
+### Apple Silicon (llama.cpp / Metal)
-## LaTeX OCR
+`llama-server` with Metal backend.
-| Method | edit ⬇ | time taken (s) ⬇ |
-|--------|----------|------------------|
-| texify | 0.122617 | 35.6345 |
+| `--parallel` | Pages/s | Tokens/s | p50 (ms) | p95 (ms) | avg tok/page | Power |
+|-------------:|---------:|---------:|---:|---:|---:|---:|
+| 8 | 0.108 | 254 | 59,313 | 129,173 | 2,360 | ~30 W |
-This inferences texify on a ground truth set of LaTeX, then does edit distance. This is a bit noisy, since 2 LaTeX strings that render the same can have different symbols in them.
-
-## Running your own benchmarks
-
-You can benchmark the performance of surya on your machine.
-
-- Follow the manual install instructions above.
-- `poetry install --group dev` - installs dev dependencies
-
-**Text line detection**
-
-This will evaluate tesseract and surya for text line detection across a randomly sampled set of images from [doclaynet](https://huggingface.co/datasets/vikp/doclaynet_bench).
-
-```shell
-python benchmark/detection.py --max_rows 256
-```
+## Reproducing
-- `--max_rows` controls how many images to process for the benchmark
-- `--debug` will render images and detected bboxes
-- `--pdf_path` will let you specify a pdf to benchmark instead of the default data
-- `--results_dir` will let you specify a directory to save results to instead of the default one
-
-**Text recognition**
-
-This will evaluate surya and optionally tesseract on multilingual pdfs from common crawl (with synthetic data for missing languages).
-
-```shell
-python benchmark/recognition.py --tesseract
-```
-
-- `--max_rows` controls how many images to process for the benchmark
-- `--debug 2` will render images with detected text
-- `--results_dir` will let you specify a directory to save results to instead of the default one
-- `--tesseract` will run the benchmark with tesseract. You have to run `sudo apt-get install tesseract-ocr-all` to install all tesseract data, and set `TESSDATA_PREFIX` to the path to the tesseract data folder.
-
-- Set `RECOGNITION_BATCH_SIZE=864` to use the same batch size as the benchmark.
-- Set `RECOGNITION_BENCH_DATASET_NAME=vikp/rec_bench_hist` to use the historical document data for benchmarking. This data comes from the [tapuscorpus](https://github.com/HTR-United/tapuscorpus).
-
-**Layout analysis**
-
-This will evaluate surya on the publaynet dataset.
-
-```shell
-python benchmark/layout.py
-```
-
-- `--max_rows` controls how many images to process for the benchmark
-- `--debug` will render images with detected text
-- `--results_dir` will let you specify a directory to save results to instead of the default one
-
-**Reading Order**
-
-```shell
-python benchmark/ordering.py
-```
-
-- `--max_rows` controls how many images to process for the benchmark
-- `--debug` will render images with detected text
-- `--results_dir` will let you specify a directory to save results to instead of the default one
-
-**Table Recognition**
-
-```shell
-python benchmark/table_recognition.py --max_rows 1024 --tatr
-```
-
-- `--max_rows` controls how many images to process for the benchmark
-- `--debug` will render images with detected text
-- `--results_dir` will let you specify a directory to save results to instead of the default one
-- `--tatr` specifies whether to also run table transformer
-
-**LaTeX OCR**
-
-```shell
-python benchmark/texify.py --max_rows 128
-```
-
-- `--max_rows` controls how many images to process for the benchmark
-- `--results_dir` will let you specify a directory to save results to instead of the default one
+We score Surya 2 on olmOCR-bench by serving the model with `vllm` (or
+`llama.cpp`) and running the olmOCR-bench harness from
+[allenai/olmocr](https://github.com/allenai/olmocr), with some adjustments applied to account for our output HTML format.
# Training
-Text detection was trained on 4x A6000s for 3 days. It used a diverse set of images as training data. It was trained from scratch using a modified efficientvit architecture for semantic segmentation.
-
-Text recognition was trained on 4x A6000s for 2 weeks. It was trained using a modified donut model (GQA, MoE layer, UTF-16 decoding, layer config changes).
-
-# Finetuning Surya OCR
-You can now take Surya OCR further by training it on your own data with our [finetuning script](/surya/scripts/finetune_ocr.py).
-It’s built on Hugging Face Trainer, and supports all the [arguments](https://huggingface.co/docs/transformers/en/main_classes/trainer#transformers.TrainingArguments) that the huggingface trainer provides, and integrations like torchrun, or deepspeed.
-
-To setup your dataset, follow the example dataset format [here](https://huggingface.co/datasets/datalab-to/ocr_finetune_example) and provide the path to your own dataset when launching the training script.
-```bash
-# Tested on 1xH100 GPU
-# Set --pretrained_checkpoint_path to load from a custom checkpoint, otherwise
-# the default surya ocr weights will be loaded as the initialization
-python surya/scripts/finetune_ocr.py \
- --output_dir $OUTPUT_DIR \
- --dataset_name datalab-to/ocr_finetune_example \
- --per_device_train_batch_size 64 \
- --gradient_checkpointing true \
- --max_sequence_length 1024
-```
+Layout, OCR, and table recognition all share a single vision-language model
+(Qwen3.5-style architecture, ~650M params). It's trained on diverse document
+images to emit either a layout JSON or a full-page HTML output, depending on
+prompt. Text-line detection is a separate small torch model — a modified
+EfficientViT segformer trained from scratch on document line annotations.
-This is a minimal training script to get you started finetuning Surya. Our internal training stack includes character bounding box finetuning, sliding window attention with specialized attention masks, custom kernels, augmentations, and other optimizations that can push OCR accuracy well beyond standard finetuning. If you want to get the most out of your data, reach us at hi@datalab.to!
+If you want help finetuning Surya on your own data, or to use our managed
+training stack, reach us at hi@datalab.to.
# Thanks
This work would not have been possible without amazing open source AI work:
+- [Qwen3-VL](https://huggingface.co/Qwen) from Alibaba
+- [vllm](https://github.com/vllm-project/vllm) and [llama.cpp](https://github.com/ggerganov/llama.cpp) for inference
- [Segformer](https://arxiv.org/pdf/2105.15203.pdf) from NVIDIA
- [EfficientViT](https://github.com/mit-han-lab/efficientvit) from MIT
- [timm](https://github.com/huggingface/pytorch-image-models) from Ross Wightman
-- [Donut](https://github.com/clovaai/donut) from Naver
- [transformers](https://github.com/huggingface/transformers) from huggingface
- [CRAFT](https://github.com/clovaai/CRAFT-pytorch), a great scene text detection model
@@ -614,6 +499,6 @@ If you use surya (or the associated models) in your work or research, please con
author = {Vikas Paruchuri and Datalab Team},
title = {Surya: A lightweight document OCR and analysis toolkit},
year = {2025},
- howpublished = {\url{https://github.com/VikParuchuri/surya}},
+ howpublished = {\url{https://github.com/datalab-to/surya}},
note = {GitHub repository},
}
diff --git a/benchmark/detection.py b/benchmark/detection.py
deleted file mode 100644
index 999cc5d5..00000000
--- a/benchmark/detection.py
+++ /dev/null
@@ -1,138 +0,0 @@
-import argparse
-import collections
-import copy
-import json
-
-import click
-
-from benchmark.utils.bbox import get_pdf_lines
-from benchmark.utils.metrics import precision_recall
-from benchmark.utils.tesseract import tesseract_parallel
-from surya.input.processing import open_pdf, get_page_images, convert_if_not_rgb
-from surya.debug.draw import draw_polys_on_image
-from surya.common.util import rescale_bbox
-from surya.settings import settings
-from surya.detection import DetectionPredictor
-
-import os
-import time
-from tabulate import tabulate
-import datasets
-
-
-@click.command(help="Benchmark detection model.")
-@click.option("--pdf_path", type=str, help="Path to PDF to detect bboxes in.", default=None)
-@click.option("--results_dir", type=str, help="Path to JSON file with OCR results.", default=os.path.join(settings.RESULT_DIR, "benchmark"))
-@click.option("--max_rows", type=int, help="Maximum number of pdf pages to OCR.", default=100)
-@click.option("--debug", is_flag=True, help="Enable debug mode.", default=False)
-@click.option("--tesseract", is_flag=True, help="Run tesseract as well.", default=False)
-def main(pdf_path: str, results_dir: str, max_rows: int, debug: bool, tesseract: bool):
- det_predictor = DetectionPredictor()
-
- if pdf_path is not None:
- pathname = pdf_path
- doc = open_pdf(pdf_path)
- page_count = len(doc)
- page_indices = list(range(page_count))
- page_indices = page_indices[:max_rows]
-
- images = get_page_images(doc, page_indices)
- doc.close()
-
- image_sizes = [img.size for img in images]
- correct_boxes = get_pdf_lines(pdf_path, image_sizes)
- else:
- pathname = "det_bench"
- # These have already been shuffled randomly, so sampling from the start is fine
- dataset = datasets.load_dataset(settings.DETECTOR_BENCH_DATASET_NAME, split=f"train[:{max_rows}]")
- images = list(dataset["image"])
- images = convert_if_not_rgb(images)
- correct_boxes = []
- for i, boxes in enumerate(dataset["bboxes"]):
- img_size = images[i].size
- # 1000,1000 is bbox size for doclaynet
- correct_boxes.append([rescale_bbox(b, (1000, 1000), img_size) for b in boxes])
-
- if settings.DETECTOR_STATIC_CACHE:
- # Run through one batch to compile the model
- det_predictor(images[:1])
-
- start = time.time()
- predictions = det_predictor(images)
- surya_time = time.time() - start
-
- if tesseract:
- start = time.time()
- tess_predictions = tesseract_parallel(images)
- tess_time = time.time() - start
- else:
- tess_predictions = [None] * len(images)
- tess_time = None
-
- folder_name = os.path.basename(pathname).split(".")[0]
- result_path = os.path.join(results_dir, folder_name)
- os.makedirs(result_path, exist_ok=True)
-
- page_metrics = collections.OrderedDict()
- for idx, (tb, sb, cb) in enumerate(zip(tess_predictions, predictions, correct_boxes)):
- surya_boxes = [s.bbox for s in sb.bboxes]
- surya_polys = [s.polygon for s in sb.bboxes]
-
- surya_metrics = precision_recall(surya_boxes, cb)
- if tb is not None:
- tess_metrics = precision_recall(tb, cb)
- else:
- tess_metrics = None
-
- page_metrics[idx] = {
- "surya": surya_metrics,
- "tesseract": tess_metrics
- }
-
- if debug:
- bbox_image = draw_polys_on_image(surya_polys, copy.deepcopy(images[idx]))
- bbox_image.save(os.path.join(result_path, f"{idx}_bbox.png"))
-
- mean_metrics = {}
- metric_types = sorted(page_metrics[0]["surya"].keys())
- models = ["surya"]
- if tesseract:
- models.append("tesseract")
-
- for k in models:
- for m in metric_types:
- metric = []
- for page in page_metrics:
- metric.append(page_metrics[page][k][m])
- if k not in mean_metrics:
- mean_metrics[k] = {}
- mean_metrics[k][m] = sum(metric) / len(metric)
-
- out_data = {
- "times": {
- "surya": surya_time,
- "tesseract": tess_time
- },
- "metrics": mean_metrics,
- "page_metrics": page_metrics
- }
-
- with open(os.path.join(result_path, "results.json"), "w+", encoding="utf-8") as f:
- json.dump(out_data, f, indent=4)
-
- table_headers = ["Model", "Time (s)", "Time per page (s)"] + metric_types
- table_data = [
- ["surya", surya_time, surya_time / len(images)] + [mean_metrics["surya"][m] for m in metric_types],
- ]
- if tesseract:
- table_data.append(
- ["tesseract", tess_time, tess_time / len(images)] + [mean_metrics["tesseract"][m] for m in metric_types]
- )
-
- print(tabulate(table_data, headers=table_headers, tablefmt="github"))
- print("Precision and recall are over the mutual coverage of the detected boxes and the ground truth boxes at a .5 threshold. There is a precision penalty for multiple boxes overlapping reference lines.")
- print(f"Wrote results to {result_path}")
-
-
-if __name__ == "__main__":
- main()
diff --git a/benchmark/layout.py b/benchmark/layout.py
deleted file mode 100644
index 63ec85f7..00000000
--- a/benchmark/layout.py
+++ /dev/null
@@ -1,156 +0,0 @@
-import collections
-import copy
-import json
-
-import click
-
-from benchmark.utils.metrics import precision_recall
-from surya.foundation import FoundationPredictor
-from surya.layout import LayoutPredictor
-from surya.input.processing import convert_if_not_rgb
-from surya.debug.draw import draw_bboxes_on_image
-from surya.settings import settings
-import os
-import time
-from tabulate import tabulate
-import datasets
-
-
-@click.command(help="Benchmark surya layout model.")
-@click.option(
- "--results_dir",
- type=str,
- help="Path to JSON file with OCR results.",
- default=os.path.join(settings.RESULT_DIR, "benchmark"),
-)
-@click.option(
- "--max_rows",
- type=int,
- help="Maximum number of images to run benchmark on.",
- default=100,
-)
-@click.option("--debug", is_flag=True, help="Run in debug mode.", default=False)
-def main(results_dir: str, max_rows: int, debug: bool):
- foundation_predictor = FoundationPredictor(checkpoint=settings.LAYOUT_MODEL_CHECKPOINT)
- layout_predictor = LayoutPredictor(foundation_predictor)
-
- pathname = "layout_bench"
- # These have already been shuffled randomly, so sampling from the start is fine
- dataset = datasets.load_dataset(
- settings.LAYOUT_BENCH_DATASET_NAME, split=f"train[:{max_rows}]"
- )
- images = list(dataset["image"])
- images = convert_if_not_rgb(images)
-
- if settings.LAYOUT_STATIC_CACHE:
- layout_predictor(images[:1])
-
- start = time.time()
- layout_predictions = layout_predictor(images)
- surya_time = time.time() - start
-
- folder_name = os.path.basename(pathname).split(".")[0]
- result_path = os.path.join(results_dir, folder_name)
- os.makedirs(result_path, exist_ok=True)
-
- label_alignment = { # First is publaynet, second is surya
- "Image": [["Figure"], ["Picture", "Figure"]],
- "Table": [["Table"], ["Table", "Form", "TableOfContents"]],
- "Text": [
- ["Text"],
- [
- "Text",
- "Formula",
- "Footnote",
- "Caption",
- "TextInlineMath",
- "Code",
- "Handwriting",
- ],
- ],
- "List": [["List"], ["ListItem"]],
- "Title": [["Title"], ["SectionHeader", "Title"]],
- }
-
- page_metrics = collections.OrderedDict()
- for idx, pred in enumerate(layout_predictions):
- row = dataset[idx]
- all_correct_bboxes = []
- page_results = {}
- for label_name in label_alignment:
- correct_cats, surya_cats = label_alignment[label_name]
- correct_bboxes = [
- b
- for b, category in zip(row["bboxes"], row["labels"])
- if category in correct_cats
- ]
- all_correct_bboxes.extend(correct_bboxes)
- pred_bboxes = [b.bbox for b in pred.bboxes if b.label in surya_cats]
-
- metrics = precision_recall(
- pred_bboxes, correct_bboxes, penalize_double=False
- )
- weight = len(correct_bboxes)
- metrics["weight"] = weight
- page_results[label_name] = metrics
-
- page_metrics[idx] = page_results
-
- if debug:
- bbox_image = draw_bboxes_on_image(
- all_correct_bboxes, copy.deepcopy(images[idx])
- )
- bbox_image.save(os.path.join(result_path, f"{idx}_layout.png"))
-
- mean_metrics = collections.defaultdict(dict)
- layout_types = sorted(page_metrics[0].keys())
- metric_types = sorted(page_metrics[0][layout_types[0]].keys())
- metric_types.remove("weight")
- for label in layout_types:
- for m in metric_types:
- metric = []
- total = 0
- for page in page_metrics:
- metric.append(
- page_metrics[page][label][m] * page_metrics[page][label]["weight"]
- )
- total += page_metrics[page][label]["weight"]
-
- value = sum(metric)
- if value > 0:
- value /= total
- mean_metrics[label][m] = value
-
- out_data = {
- "time": surya_time,
- "metrics": mean_metrics,
- "page_metrics": page_metrics,
- }
-
- with open(os.path.join(result_path, "results.json"), "w+", encoding="utf-8") as f:
- json.dump(out_data, f, indent=4)
-
- table_headers = [
- "Layout Type",
- ] + metric_types
- table_data = []
- for layout_type in layout_types:
- table_data.append(
- [
- layout_type,
- ]
- + [f"{mean_metrics[layout_type][m]:.5f}" for m in metric_types]
- )
-
- print(tabulate(table_data, headers=table_headers, tablefmt="github"))
- print(
- f"Took {surya_time / len(images):.5f} seconds per image, and {surya_time:.5f} seconds total."
- )
- print(
- "Precision and recall are over the mutual coverage of the detected boxes and the ground truth boxes at a .5 threshold."
- )
- print(f"Wrote results to {result_path}")
-
-
-if __name__ == "__main__":
- main()
diff --git a/benchmark/ordering.py b/benchmark/ordering.py
deleted file mode 100644
index 992339c8..00000000
--- a/benchmark/ordering.py
+++ /dev/null
@@ -1,92 +0,0 @@
-import collections
-import json
-
-import click
-
-from surya.foundation import FoundationPredictor
-from surya.input.processing import convert_if_not_rgb
-from surya.layout import LayoutPredictor
-from surya.common.polygon import PolygonBox
-from surya.settings import settings
-from benchmark.utils.metrics import rank_accuracy
-import os
-import time
-import datasets
-
-
-@click.command(help="Benchmark surya layout for reading order.")
-@click.option(
- "--results_dir",
- type=str,
- help="Path to JSON file with benchmark results.",
- default=os.path.join(settings.RESULT_DIR, "benchmark"),
-)
-@click.option(
- "--max_rows",
- type=int,
- help="Maximum number of images to run benchmark on.",
- default=None,
-)
-def main(results_dir: str, max_rows: int):
- foundation_predictor = FoundationPredictor(checkpoint=settings.LAYOUT_MODEL_CHECKPOINT)
- layout_predictor = LayoutPredictor(foundation_predictor)
- pathname = "order_bench"
- # These have already been shuffled randomly, so sampling from the start is fine
- split = "train"
- if max_rows is not None:
- split = f"train[:{max_rows}]"
- dataset = datasets.load_dataset(settings.ORDER_BENCH_DATASET_NAME, split=split)
- images = list(dataset["image"])
- images = convert_if_not_rgb(images)
-
- start = time.time()
- layout_predictions = layout_predictor(images)
- surya_time = time.time() - start
-
- folder_name = os.path.basename(pathname).split(".")[0]
- result_path = os.path.join(results_dir, folder_name)
- os.makedirs(result_path, exist_ok=True)
-
- page_metrics = collections.OrderedDict()
- mean_accuracy = 0
- for idx, order_pred in enumerate(layout_predictions):
- row = dataset[idx]
- labels = row["labels"]
- bboxes = row["bboxes"]
- pred_positions = []
- for label, bbox in zip(labels, bboxes):
- max_intersection = 0
- matching_idx = 0
- for pred_box in order_pred.bboxes:
- intersection = pred_box.intersection_pct(PolygonBox(polygon=bbox))
- if intersection > max_intersection:
- max_intersection = intersection
- matching_idx = pred_box.position
- pred_positions.append(matching_idx)
- accuracy = rank_accuracy(pred_positions, labels)
- mean_accuracy += accuracy
- page_results = {"accuracy": accuracy, "box_count": len(labels)}
-
- page_metrics[idx] = page_results
-
- mean_accuracy /= len(layout_predictions)
-
- out_data = {
- "time": surya_time,
- "mean_accuracy": mean_accuracy,
- "page_metrics": page_metrics,
- }
-
- with open(os.path.join(result_path, "results.json"), "w+", encoding="utf-8") as f:
- json.dump(out_data, f, indent=4)
-
- print(f"Mean accuracy is {mean_accuracy:.2f}.")
- print(
- f"Took {surya_time / len(images):.2f} seconds per image, and {surya_time:.1f} seconds total."
- )
- print("Mean accuracy is the % of correct ranking pairs.")
- print(f"Wrote results to {result_path}")
-
-
-if __name__ == "__main__":
- main()
diff --git a/benchmark/recognition.py b/benchmark/recognition.py
deleted file mode 100644
index 091ef9a9..00000000
--- a/benchmark/recognition.py
+++ /dev/null
@@ -1,379 +0,0 @@
-import re
-import unicodedata
-from collections import defaultdict
-
-import click
-
-from benchmark.utils.scoring import overlap_score, overlap_score_exact
-from surya.input.processing import convert_if_not_rgb
-from surya.debug.text import draw_text_on_image
-from surya.foundation import FoundationPredictor
-from surya.recognition import RecognitionPredictor
-from surya.settings import settings
-from surya.recognition.languages import CODE_TO_LANGUAGE
-from benchmark.utils.tesseract import (
- tesseract_ocr_parallel,
- surya_lang_to_tesseract,
- TESS_CODE_TO_LANGUAGE,
-)
-from benchmark.utils.textract import textract_ocr_parallel
-import os
-import datasets
-import json
-import time
-from tabulate import tabulate
-
-KEY_LANGUAGES = [
- "Chinese",
- "Spanish",
- "English",
- "Arabic",
- "Hindi",
- "Bengali",
- "Russian",
- "Japanese",
-]
-
-
-def list_in(lst: str | list, lst2: list):
- if isinstance(lst, str):
- lst = [lst]
- return any([item in lst for item in lst2])
-
-
-def standardize_bullets(text):
- patterns = [
- r"•\s+",
- r"·\s+",
- r"○\s+",
- r"◦\s+",
- r"▪\s+",
- r"▫\s+",
- r"➢\s+",
- r"➤\s+",
- r"★\s+",
- r"✓\s+",
- r"✗\s+",
- r"✦\s+",
- r"\\bullet\s+",
- ]
-
- combined_pattern = "|".join(patterns)
- text = re.sub(combined_pattern, "*", text)
-
- return text
-
-
-def normalize_text(text: str) -> str:
- # Remove HTML tags
- text = re.sub(r"<[^>]+>", "", text)
- # Remove LaTeX tags
- text = re.sub(r"\\[a-zA-Z]+", "", text)
- text = standardize_bullets(text)
- text = unicodedata.normalize("NFKC", text)
- return text.strip().lower().replace(",", ".")
-
-
-@click.command(help="Benchmark recognition model.")
-@click.option(
- "--results_dir",
- type=str,
- help="Path to JSON file with OCR results.",
- default=os.path.join(settings.RESULT_DIR, "benchmark"),
-)
-@click.option(
- "--max_rows", type=int, help="Maximum number of pdf pages to OCR.", default=None
-)
-@click.option("--debug", is_flag=True, help="Enable debug mode.", default=False)
-@click.option(
- "--tesseract", is_flag=True, help="Run benchmarks on tesseract.", default=False
-)
-@click.option(
- "--textract", is_flag=True, help="Run benchmarks on textract.", default=False
-)
-@click.option(
- "--tess_cpus", type=int, help="Number of CPUs to use for tesseract.", default=28
-)
-@click.option(
- "--textract_cpus", type=int, help="Number of CPUs to use for textract.", default=28
-)
-@click.option(
- "--languages",
- type=str,
- help="Comma-separated list of languages to benchmark.",
- default=None,
-)
-@click.option(
- "--print_results",
- is_flag=True,
-)
-def main(
- results_dir: str,
- max_rows: int,
- debug: bool,
- tesseract: bool,
- textract: bool,
- tess_cpus: int,
- textract_cpus: int,
- languages: str | None,
- print_results: bool,
-):
- foundation_predictor = FoundationPredictor()
- rec_predictor = RecognitionPredictor(foundation_predictor)
-
- split = "train"
- dataset = datasets.load_dataset(
- settings.RECOGNITION_BENCH_DATASET_NAME, split=split
- )
-
- if languages:
- languages = languages.split(",")
- dataset = dataset.filter(
- lambda x: list_in(x["language"], languages), num_proc=4
- )
-
- if max_rows and max_rows < len(dataset):
- dataset = dataset.shuffle(seed=1).select(range(max_rows))
-
- images = list(dataset["image"])
- images = convert_if_not_rgb(images)
- bboxes = list(dataset["bboxes"])
- line_text = list(dataset["text"])
- languages = list(dataset["language"])
-
- print(f"Loaded {len(images)} images. Running OCR...")
-
- start = time.time()
- predictions_by_image = rec_predictor(images, None, bboxes=bboxes)
- surya_time = time.time() - start
-
- lang_list = []
- for lang in languages:
- if not isinstance(lang, list):
- lang_list.append([lang])
- else:
- lang_list.append(lang)
-
- surya_scores = defaultdict(list)
- img_surya_scores = []
- outputs = []
- for idx, (pred, ref_text, langs) in enumerate(
- zip(predictions_by_image, line_text, lang_list)
- ):
- pred_text = [line.text for line in pred.text_lines]
-
- score_ref_text = [normalize_text(line) for line in ref_text]
- score_pred_text = [normalize_text(text) for text in pred_text]
- image_scores, image_weights = overlap_score_exact(
- score_pred_text, score_ref_text
- )
- normalized_scores = [
- score / max(1, weight) for score, weight in zip(image_scores, image_weights)
- ]
- image_score = sum(image_scores) / max(1, sum(image_weights))
-
- img_surya_scores.append(image_score)
- for lang in langs:
- surya_scores[CODE_TO_LANGUAGE[lang]].append(image_score)
-
- assert len(pred_text) == len(ref_text) == len(bboxes[idx])
- if debug:
- for j, (pred_line, ref_line, score, bbox) in enumerate(
- zip(pred_text, ref_text, normalized_scores, bboxes[idx])
- ):
- image_slice = images[idx].crop(bbox)
-
- outputs.append(
- {
- "image": image_slice,
- "bbox": bbox,
- "score": score,
- "pred": pred_line,
- "ref": ref_line,
- "langs": ",".join(langs),
- }
- )
-
- if debug:
- out_ds = datasets.Dataset.from_list(outputs)
- out_ds.push_to_hub("datalab-to/rec_bench_outputs", private=True)
-
- flat_surya_scores = [score for lang in surya_scores for score in surya_scores[lang]]
- benchmark_stats = {
- "surya": {
- "avg_score": sum(flat_surya_scores) / max(1, len(flat_surya_scores)),
- "lang_scores": {
- lang: sum(scores) / max(1, len(scores))
- for lang, scores in surya_scores.items()
- },
- "time_per_img": surya_time / max(1, len(images)),
- }
- }
-
- result_path = os.path.join(results_dir, "rec_bench")
- os.makedirs(result_path, exist_ok=True)
-
- with open(os.path.join(result_path, "surya_scores.json"), "w+") as f:
- json.dump(surya_scores, f)
-
- if tesseract:
- tess_valid = []
- tess_langs = []
- for idx, lang in enumerate(lang_list):
- # Tesseract does not support all languages
- tess_lang = surya_lang_to_tesseract(lang[0])
- if tess_lang is None:
- continue
-
- tess_valid.append(idx)
- tess_langs.append(tess_lang)
-
- tess_imgs = [images[i] for i in tess_valid]
- tess_bboxes = [bboxes[i] for i in tess_valid]
- tess_reference = [line_text[i] for i in tess_valid]
- start = time.time()
- tess_predictions = tesseract_ocr_parallel(
- tess_imgs, tess_bboxes, tess_langs, cpus=tess_cpus
- )
- tesseract_time = time.time() - start
-
- tess_scores = defaultdict(list)
- for idx, (pred, ref_text, lang) in enumerate(
- zip(tess_predictions, tess_reference, tess_langs)
- ):
- image_scores, image_weights, _ = overlap_score(pred, ref_text)
- image_score = sum(image_scores) / max(1, sum(image_weights))
- tess_scores[TESS_CODE_TO_LANGUAGE[lang]].append(image_score)
-
- flat_tess_scores = [
- score for lang in tess_scores for score in tess_scores[lang]
- ]
- benchmark_stats["tesseract"] = {
- "avg_score": sum(flat_tess_scores) / len(flat_tess_scores),
- "lang_scores": {
- lang: sum(scores) / len(scores) for lang, scores in tess_scores.items()
- },
- "time_per_img": tesseract_time / len(tess_imgs),
- }
-
- with open(os.path.join(result_path, "tesseract_scores.json"), "w+") as f:
- json.dump(tess_scores, f)
-
- if textract:
- start = time.time()
- textract_predictions = textract_ocr_parallel(images, cpus=textract_cpus)
- textract_time = time.time() - start
-
- textract_scores = defaultdict(list)
- for idx, (pred, ref_text, lang) in enumerate(
- zip(textract_predictions, line_text, lang_list)
- ):
- image_scores, image_weights, _ = overlap_score(pred, ref_text)
- image_score = sum(image_scores) / max(1, sum(image_weights))
-
- for lang in lang:
- textract_scores[CODE_TO_LANGUAGE[lang]].append(image_score)
-
- flat_textract_scores = [
- score for lang in textract_scores for score in textract_scores[lang]
- ]
- benchmark_stats["textract"] = {
- "avg_score": sum(flat_textract_scores) / len(flat_textract_scores),
- "lang_scores": {
- lang: sum(scores) / len(scores)
- for lang, scores in textract_scores.items()
- },
- "time_per_img": textract_time / len(images),
- }
- print(len(flat_textract_scores))
-
- with open(os.path.join(result_path, "textract_scores.json"), "w+") as f:
- json.dump(textract_scores, f)
-
- with open(os.path.join(result_path, "results.json"), "w+", encoding="utf-8") as f:
- json.dump(benchmark_stats, f)
-
- key_languages = [k for k in KEY_LANGUAGES if k in surya_scores]
- table_headers = ["Model", "Time per page (s)", "Avg Score"] + key_languages
- table_data = [
- [
- "surya",
- benchmark_stats["surya"]["time_per_img"],
- benchmark_stats["surya"]["avg_score"],
- ]
- + [benchmark_stats["surya"]["lang_scores"][lang] for lang in key_languages],
- ]
- if tesseract:
- table_data.append(
- [
- "tesseract",
- benchmark_stats["tesseract"]["time_per_img"],
- benchmark_stats["tesseract"]["avg_score"],
- ]
- + [
- benchmark_stats["tesseract"]["lang_scores"].get(lang, 0)
- for lang in key_languages
- ]
- )
- if textract:
- table_data.append(
- [
- "textract",
- benchmark_stats["textract"]["time_per_img"],
- benchmark_stats["textract"]["avg_score"],
- ]
- + [
- benchmark_stats["textract"]["lang_scores"][lang]
- for lang in key_languages
- ],
- )
-
- print(tabulate(table_data, headers=table_headers, tablefmt="github"))
- print(
- "Only a few major languages are displayed. See the result path for additional languages."
- )
-
- if debug >= 1:
- bad_detections = []
- for idx, (score, lang) in enumerate(zip(flat_surya_scores, lang_list)):
- if score < 0.8:
- bad_detections.append((idx, lang, score))
- print(f"Found {len(bad_detections)} bad detections. Writing to file...")
- with open(os.path.join(result_path, "bad_detections.json"), "w+") as f:
- json.dump(bad_detections, f)
-
- if debug == 2:
- for idx, (image, pred, ref_text, bbox, lang) in enumerate(
- zip(images, predictions_by_image, line_text, bboxes, lang_list)
- ):
- pred_image_name = f"{'_'.join(lang)}_{idx}_pred.png"
- ref_image_name = f"{'_'.join(lang)}_{idx}_ref.png"
- pred_text = [line.text for line in pred.text_lines]
- pred_image = draw_text_on_image(bbox, pred_text, image.size)
- pred_image.save(os.path.join(result_path, pred_image_name))
- ref_image = draw_text_on_image(bbox, ref_text, image.size)
- ref_image.save(os.path.join(result_path, ref_image_name))
- image.save(os.path.join(result_path, f"{'_'.join(lang)}_{idx}_image.png"))
-
- print(f"Wrote results to {result_path}")
-
- if print_results:
- for idx, (pred, ref_text) in enumerate(zip(predictions_by_image, line_text)):
- print(f"Image {idx}")
- print("----")
- for line_idx, (pred_line, ref_line) in enumerate(
- zip(pred.text_lines, ref_text)
- ):
- print(f"Sample {line_idx}")
- print(f"Pred: {pred_line.text}")
- print(f"Ref: {ref_line}")
- print()
-
- if settings.TORCH_DEVICE == "xla":
- import torch_xla.debug.metrics as met
-
- print(met.short_metrics_report())
-
-
-if __name__ == "__main__":
- main()
diff --git a/benchmark/table_recognition.py b/benchmark/table_recognition.py
deleted file mode 100644
index ed2809e6..00000000
--- a/benchmark/table_recognition.py
+++ /dev/null
@@ -1,196 +0,0 @@
-import click
-import collections
-import json
-
-from surya.debug.draw import draw_bboxes_on_image
-from tabulate import tabulate
-
-from surya.input.processing import convert_if_not_rgb
-from surya.table_rec import TableRecPredictor
-from surya.settings import settings
-from benchmark.utils.metrics import penalized_iou_score
-from benchmark.utils.tatr import load_tatr, batch_inference_tatr
-import os
-import time
-import datasets
-
-
-@click.command(help="Benchmark table rec dataset")
-@click.option(
- "--results_dir",
- type=str,
- help="Path to JSON file with benchmark results.",
- default=os.path.join(settings.RESULT_DIR, "benchmark"),
-)
-@click.option(
- "--max_rows",
- type=int,
- help="Maximum number of images to run benchmark on.",
- default=512,
-)
-@click.option("--tatr", is_flag=True, help="Run table transformer.", default=False)
-@click.option("--debug", is_flag=True, help="Enable debug mode.", default=False)
-def main(results_dir: str, max_rows: int, tatr: bool, debug: bool):
- table_rec_predictor = TableRecPredictor()
-
- pathname = "table_rec_bench"
- # These have already been shuffled randomly, so sampling from the start is fine
- split = "train"
- if max_rows is not None:
- split = f"train[:{max_rows}]"
- dataset = datasets.load_dataset(settings.TABLE_REC_BENCH_DATASET_NAME, split=split)
- images = list(dataset["image"])
- images = convert_if_not_rgb(images)
-
- if settings.TABLE_REC_STATIC_CACHE:
- # Run through one batch to compile the model
- table_rec_predictor(images[:1])
-
- start = time.time()
- table_rec_predictions = table_rec_predictor(images)
- surya_time = time.time() - start
-
- folder_name = os.path.basename(pathname).split(".")[0]
- result_path = os.path.join(results_dir, folder_name)
- os.makedirs(result_path, exist_ok=True)
-
- page_metrics = collections.OrderedDict()
- mean_col_iou = 0
- mean_row_iou = 0
- for idx, (pred, image) in enumerate(zip(table_rec_predictions, images)):
- row = dataset[idx]
- pred_row_boxes = [p.bbox for p in pred.rows]
- pred_col_bboxes = [p.bbox for p in pred.cols]
- actual_row_bboxes = [r["bbox"] for r in row["rows"]]
- actual_col_bboxes = [c["bbox"] for c in row["columns"]]
- row_score = penalized_iou_score(pred_row_boxes, actual_row_bboxes)
- col_score = penalized_iou_score(pred_col_bboxes, actual_col_bboxes)
- page_results = {
- "row_score": row_score,
- "col_score": col_score,
- "row_count": len(actual_row_bboxes),
- "col_count": len(actual_col_bboxes),
- }
-
- mean_col_iou += col_score
- mean_row_iou += row_score
-
- page_metrics[idx] = page_results
-
- if debug:
- # Save debug images
- draw_img = image.copy()
- draw_bboxes_on_image(
- pred_row_boxes,
- draw_img,
- [f"Row {i}" for i in range(len(pred_row_boxes))],
- )
- draw_bboxes_on_image(
- pred_col_bboxes,
- draw_img,
- [f"Col {i}" for i in range(len(pred_col_bboxes))],
- color="blue",
- )
- draw_img.save(os.path.join(result_path, f"{idx}_bbox.png"))
-
- actual_draw_image = image.copy()
- draw_bboxes_on_image(
- actual_row_bboxes,
- actual_draw_image,
- [f"Row {i}" for i in range(len(actual_row_bboxes))],
- )
- draw_bboxes_on_image(
- actual_col_bboxes,
- actual_draw_image,
- [f"Col {i}" for i in range(len(actual_col_bboxes))],
- color="blue",
- )
- actual_draw_image.save(os.path.join(result_path, f"{idx}_actual.png"))
-
- mean_col_iou /= len(table_rec_predictions)
- mean_row_iou /= len(table_rec_predictions)
-
- out_data = {
- "surya": {
- "time": surya_time,
- "mean_row_iou": mean_row_iou,
- "mean_col_iou": mean_col_iou,
- "page_metrics": page_metrics,
- }
- }
-
- if tatr:
- tatr_model = load_tatr()
- start = time.time()
- tatr_predictions = batch_inference_tatr(tatr_model, images, 1)
- tatr_time = time.time() - start
-
- page_metrics = collections.OrderedDict()
- mean_col_iou = 0
- mean_row_iou = 0
- for idx, pred in enumerate(tatr_predictions):
- row = dataset[idx]
- pred_row_boxes = [p["bbox"] for p in pred["rows"]]
- pred_col_bboxes = [p["bbox"] for p in pred["cols"]]
- actual_row_bboxes = [r["bbox"] for r in row["rows"]]
- actual_col_bboxes = [c["bbox"] for c in row["columns"]]
- row_score = penalized_iou_score(pred_row_boxes, actual_row_bboxes)
- col_score = penalized_iou_score(pred_col_bboxes, actual_col_bboxes)
- page_results = {
- "row_score": row_score,
- "col_score": col_score,
- "row_count": len(actual_row_bboxes),
- "col_count": len(actual_col_bboxes),
- }
-
- mean_col_iou += col_score
- mean_row_iou += row_score
-
- page_metrics[idx] = page_results
-
- mean_col_iou /= len(tatr_predictions)
- mean_row_iou /= len(tatr_predictions)
-
- out_data["tatr"] = {
- "time": tatr_time,
- "mean_row_iou": mean_row_iou,
- "mean_col_iou": mean_col_iou,
- "page_metrics": page_metrics,
- }
-
- with open(os.path.join(result_path, "results.json"), "w+", encoding="utf-8") as f:
- json.dump(out_data, f, indent=4)
-
- table = [
- ["Model", "Row Intersection", "Col Intersection", "Time Per Image"],
- [
- "Surya",
- f"{out_data['surya']['mean_row_iou']:.2f}",
- f"{out_data['surya']['mean_col_iou']:.5f}",
- f"{surya_time / len(images):.5f}",
- ],
- ]
-
- if tatr:
- table.append(
- [
- "Table transformer",
- f"{out_data['tatr']['mean_row_iou']:.2f}",
- f"{out_data['tatr']['mean_col_iou']:.5f}",
- f"{tatr_time / len(images):.5f}",
- ]
- )
-
- print(tabulate(table, headers="firstrow", tablefmt="github"))
-
- print(
- "Intersection is the average of the intersection % between each actual row/column, and the predictions. With penalties for too many/few predictions."
- )
- print(
- "Note that table transformers is unbatched, since the example code in the repo is unbatched."
- )
- print(f"Wrote results to {result_path}")
-
-
-if __name__ == "__main__":
- main()
diff --git a/benchmark/texify.py b/benchmark/texify.py
deleted file mode 100644
index 0e421677..00000000
--- a/benchmark/texify.py
+++ /dev/null
@@ -1,117 +0,0 @@
-import os.path
-import re
-import time
-from pathlib import Path
-from typing import List
-
-import click
-import datasets
-from tabulate import tabulate
-from bs4 import BeautifulSoup
-
-from surya.common.surya.schema import TaskNames
-from surya.settings import settings
-from surya.foundation import FoundationPredictor
-from surya.recognition import RecognitionPredictor, OCRResult
-import json
-from rapidfuzz.distance import Levenshtein
-
-
-def normalize_text(text):
- soup = BeautifulSoup(text, "html.parser")
- # Unwrap math tags
- for tag in soup.find_all():
- if tag.name == "math":
- tag.unwrap()
- text = soup.get_text()
- text = re.sub(r"\n", " ", text)
- text = re.sub(r"\s+", " ", text)
- return text.strip()
-
-
-def score_text(predictions, references):
- lev_dist = []
- for p, r in zip(predictions, references):
- p = normalize_text(p)
- r = normalize_text(r)
- lev_dist.append(Levenshtein.normalized_distance(p, r))
-
- return sum(lev_dist) / len(lev_dist)
-
-
-def inference_texify(
- source_data, predictor: RecognitionPredictor, line_mode: bool = False
-):
- images = [sd["image"] for sd in source_data]
- mode = TaskNames.ocr_with_boxes if line_mode else TaskNames.block_without_boxes
- tasks = [mode] * len(images)
- bboxes = [[[0, 0, image.width, image.height]] for image in images]
- texify_predictions: List[OCRResult] = predictor(images, tasks, bboxes=bboxes)
- out_data = [
- {
- "text": texify_predictions[i].text_lines[0].text,
- "equation": source_data[i]["equation"],
- }
- for i in range(len(texify_predictions))
- ]
-
- return out_data
-
-
-@click.command(help="Benchmark the performance of texify.")
-@click.option(
- "--ds_name",
- type=str,
- help="Path to dataset file with source images/equations.",
- default=settings.TEXIFY_BENCHMARK_DATASET,
-)
-@click.option(
- "--results_dir",
- type=str,
- help="Path to JSON file with benchmark results.",
- default=os.path.join(settings.RESULT_DIR, "benchmark"),
-)
-@click.option(
- "--max_rows", type=int, help="Maximum number of images to benchmark.", default=None
-)
-@click.option(
- "--line_mode", is_flag=True, help="Use line mode for texify.", default=False
-)
-def main(ds_name: str, results_dir: str, max_rows: int, line_mode: bool):
- foundation_predictor = FoundationPredictor()
- predictor = RecognitionPredictor(foundation_predictor)
- ds = datasets.load_dataset(ds_name, split="train")
-
- if max_rows:
- ds = ds.filter(lambda x, idx: idx < max_rows, with_indices=True)
-
- start = time.time()
- predictions = inference_texify(ds, predictor, line_mode)
- time_taken = time.time() - start
-
- text = [p["text"] for p in predictions]
- references = [p["equation"] for p in predictions]
- scores = score_text(text, references)
-
- write_data = {
- "scores": scores,
- "text": [{"prediction": p, "reference": r} for p, r in zip(text, references)],
- }
-
- score_table = [["texify", write_data["scores"], time_taken]]
- score_headers = ["edit", "time taken (s)"]
- score_dirs = ["⬇", "⬇"]
-
- score_headers = [f"{h} {d}" for h, d in zip(score_headers, score_dirs)]
- table = tabulate(score_table, headers=["Method", *score_headers])
- print()
- print(table)
-
- result_path = Path(results_dir) / "texify_bench"
- result_path.mkdir(parents=True, exist_ok=True)
- with open(result_path / "results.json", "w", encoding="utf-8") as f:
- json.dump(write_data, f, indent=4)
-
-
-if __name__ == "__main__":
- main()
diff --git a/benchmark/utils/__init__.py b/benchmark/utils/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/benchmark/utils/bbox.py b/benchmark/utils/bbox.py
deleted file mode 100644
index 2438c980..00000000
--- a/benchmark/utils/bbox.py
+++ /dev/null
@@ -1,54 +0,0 @@
-import fitz as pymupdf
-from surya.common.util import rescale_bbox
-
-
-def get_pdf_lines(pdf_path, img_sizes):
- doc = pymupdf.open(pdf_path)
- page_lines = []
- for idx, img_size in enumerate(img_sizes):
- page = doc[idx]
- blocks = page.get_text("dict", sort=True, flags=pymupdf.TEXTFLAGS_DICT & ~pymupdf.TEXT_PRESERVE_LIGATURES & ~pymupdf.TEXT_PRESERVE_IMAGES)["blocks"]
-
- line_boxes = []
- for block_idx, block in enumerate(blocks):
- for l in block["lines"]:
- line_boxes.append(list(l["bbox"]))
-
- page_box = page.bound()
- pwidth, pheight = page_box[2] - page_box[0], page_box[3] - page_box[1]
- line_boxes = [rescale_bbox(bbox, (pwidth, pheight), img_size) for bbox in line_boxes]
- page_lines.append(line_boxes)
-
- return page_lines
-
-def merge_boxes(box1, box2):
- return (min(box1[0], box2[0]), min(box1[1], box2[1]), max(box1[2], box2[2]), max(box1[3], box2[3]))
-
-
-def join_lines(bboxes, max_gap=5):
- to_merge = {}
- for i, box1 in bboxes:
- for z, box2 in bboxes[i + 1:]:
- j = i + z + 1
- if box1 == box2:
- continue
-
- if box1[0] <= box2[0] and box1[2] >= box2[2]:
- if abs(box1[1] - box2[3]) <= max_gap:
- if i not in to_merge:
- to_merge[i] = []
- to_merge[i].append(j)
-
- merged_boxes = set()
- merged = []
- for i, box in bboxes:
- if i in merged_boxes:
- continue
-
- if i in to_merge:
- for j in to_merge[i]:
- box = merge_boxes(box, bboxes[j][1])
- merged_boxes.add(j)
-
- merged.append(box)
- return merged
diff --git a/benchmark/utils/metrics.py b/benchmark/utils/metrics.py
deleted file mode 100644
index 447d679a..00000000
--- a/benchmark/utils/metrics.py
+++ /dev/null
@@ -1,205 +0,0 @@
-from functools import partial
-from itertools import repeat
-
-import numpy as np
-from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
-
-
-def box_area(box):
- return (box[2] - box[0]) * (box[3] - box[1])
-
-
-def calculate_iou(box1, box2, box1_only=False):
- intersection = intersection_area(box1, box2)
- union = box_area(box1)
- if not box1_only:
- union += box_area(box2) - intersection
-
- if union == 0:
- return 0
- return intersection / union
-
-
-def match_boxes(preds, references):
- num_actual = len(references)
- num_predicted = len(preds)
-
- iou_matrix = np.zeros((num_actual, num_predicted))
- for i, actual in enumerate(references):
- for j, pred in enumerate(preds):
- iou_matrix[i, j] = calculate_iou(actual, pred, box1_only=True)
-
- sorted_indices = np.argsort(iou_matrix, axis=None)[::-1]
- sorted_ious = iou_matrix.flatten()[sorted_indices]
- actual_indices, predicted_indices = np.unravel_index(sorted_indices, iou_matrix.shape)
-
- assigned_actual = set()
- assigned_pred = set()
-
- matches = []
- for idx, iou in zip(zip(actual_indices, predicted_indices), sorted_ious):
- i, j = idx
- if i not in assigned_actual and j not in assigned_pred:
- iou_val = iou_matrix[i, j]
- if iou_val > .95: # Account for rounding on box edges
- iou_val = 1.0
- matches.append((i, j, iou_val))
- assigned_actual.add(i)
- assigned_pred.add(j)
-
- unassigned_actual = set(range(num_actual)) - assigned_actual
- unassigned_pred = set(range(num_predicted)) - assigned_pred
- matches.extend([(i, None, -1.0) for i in unassigned_actual])
- matches.extend([(None, j, 0.0) for j in unassigned_pred])
-
- return matches
-
-def penalized_iou_score(preds, references):
- matches = match_boxes(preds, references)
- iou = sum([match[2] for match in matches]) / len(matches)
- return iou
-
-def intersection_pixels(box1, box2):
- x_left = max(box1[0], box2[0])
- y_top = max(box1[1], box2[1])
- x_right = min(box1[2], box2[2])
- y_bottom = min(box1[3], box2[3])
-
- if x_right < x_left or y_bottom < y_top:
- return set()
-
- x_left, x_right = int(x_left), int(x_right)
- y_top, y_bottom = int(y_top), int(y_bottom)
-
- coords = np.meshgrid(np.arange(x_left, x_right), np.arange(y_top, y_bottom))
- pixels = set(zip(coords[0].flat, coords[1].flat))
-
- return pixels
-
-
-def calculate_coverage(box, other_boxes, penalize_double=False):
- box_area = (box[2] - box[0]) * (box[3] - box[1])
- if box_area == 0:
- return 0
-
- # find total coverage of the box
- covered_pixels = set()
- double_coverage = list()
- for other_box in other_boxes:
- ia = intersection_pixels(box, other_box)
- double_coverage.append(list(covered_pixels.intersection(ia)))
- covered_pixels = covered_pixels.union(ia)
-
- # Penalize double coverage - having multiple bboxes overlapping the same pixels
- double_coverage_penalty = len(double_coverage)
- if not penalize_double:
- double_coverage_penalty = 0
- covered_pixels_count = max(0, len(covered_pixels) - double_coverage_penalty)
- return covered_pixels_count / box_area
-
-
-def intersection_area(box1, box2):
- x_left = max(box1[0], box2[0])
- y_top = max(box1[1], box2[1])
- x_right = min(box1[2], box2[2])
- y_bottom = min(box1[3], box2[3])
-
- if x_right < x_left or y_bottom < y_top:
- return 0.0
-
- return (x_right - x_left) * (y_bottom - y_top)
-
-
-def calculate_coverage_fast(box, other_boxes, penalize_double=False):
- box = np.array(box)
- other_boxes = np.array(other_boxes)
-
- # Calculate box area
- box_area = (box[2] - box[0]) * (box[3] - box[1])
- if box_area == 0:
- return 0
-
- x_left = np.maximum(box[0], other_boxes[:, 0])
- y_top = np.maximum(box[1], other_boxes[:, 1])
- x_right = np.minimum(box[2], other_boxes[:, 2])
- y_bottom = np.minimum(box[3], other_boxes[:, 3])
-
- widths = np.maximum(0, x_right - x_left)
- heights = np.maximum(0, y_bottom - y_top)
- intersect_areas = widths * heights
-
- total_intersect = np.sum(intersect_areas)
-
- return min(1.0, total_intersect / box_area)
-
-
-def precision_recall(preds, references, threshold=.5, workers=8, penalize_double=True):
- if len(references) == 0:
- return {
- "precision": 1,
- "recall": 1,
- }
-
- if len(preds) == 0:
- return {
- "precision": 0,
- "recall": 0,
- }
-
- # If we're not penalizing double coverage, we can use a faster calculation
- coverage_func = calculate_coverage_fast
- if penalize_double:
- coverage_func = calculate_coverage
-
- with ThreadPoolExecutor(max_workers=workers) as executor:
- precision_func = partial(coverage_func, penalize_double=penalize_double)
- precision_iou = executor.map(precision_func, preds, repeat(references))
- reference_iou = executor.map(coverage_func, references, repeat(preds))
-
- precision_classes = [1 if i > threshold else 0 for i in precision_iou]
- precision = sum(precision_classes) / len(precision_classes)
-
- recall_classes = [1 if i > threshold else 0 for i in reference_iou]
- recall = sum(recall_classes) / len(recall_classes)
-
- return {
- "precision": precision,
- "recall": recall,
- }
-
-
-def mean_coverage(preds, references):
- coverages = []
-
- for box1 in references:
- coverage = calculate_coverage(box1, preds)
- coverages.append(coverage)
-
- for box2 in preds:
- coverage = calculate_coverage(box2, references)
- coverages.append(coverage)
-
- # Calculate the average coverage over all comparisons
- if len(coverages) == 0:
- return 0
- coverage = sum(coverages) / len(coverages)
- return {"coverage": coverage}
-
-
-def rank_accuracy(preds, references):
- # Preds and references need to be aligned so each position refers to the same bbox
- pairs = []
- for i, pred in enumerate(preds):
- for j, pred2 in enumerate(preds):
- if i == j:
- continue
- pairs.append((i, j, pred > pred2))
-
- # Find how many of the prediction rankings are correct
- correct = 0
- for i, ref in enumerate(references):
- for j, ref2 in enumerate(references):
- if (i, j, ref > ref2) in pairs:
- correct += 1
-
- return correct / len(pairs)
\ No newline at end of file
diff --git a/benchmark/utils/scoring.py b/benchmark/utils/scoring.py
deleted file mode 100644
index d8330cf3..00000000
--- a/benchmark/utils/scoring.py
+++ /dev/null
@@ -1,40 +0,0 @@
-import math
-from typing import List
-
-from rapidfuzz import fuzz
-
-
-def overlap_score(pred_lines: List[str], reference_lines: List[str]):
- line_scores = []
- line_weights = []
- line_match = {}
- for i, pred_line in enumerate(pred_lines):
- max_score = 0
- line_weight = 1
- match = None
- for j, ref_line in enumerate(reference_lines):
- score = fuzz.ratio(pred_line, ref_line, score_cutoff=20) / 100
- if score > max_score:
- max_score = score
- line_weight = math.sqrt(len(ref_line))
- match = j
- line_scores.append(max_score)
- line_weights.append(line_weight)
- line_match[i] = match
- line_scores = [line_scores[i] * line_weights[i] for i in range(len(line_scores))]
-
- return line_scores, line_weights, line_match
-
-
-def overlap_score_exact(pred_lines: List[str], reference_lines: List[str]):
- line_scores = []
- line_weights = []
- assert len(pred_lines) == len(reference_lines)
-
- for i, (pred_line, ref_line) in enumerate(zip(pred_lines, reference_lines)):
- score = fuzz.ratio(pred_line, ref_line, score_cutoff=20) / 100
- weight = math.sqrt(len(ref_line))
- line_scores.append(score * weight)
- line_weights.append(weight)
-
- return line_scores, line_weights
diff --git a/benchmark/utils/tatr.py b/benchmark/utils/tatr.py
deleted file mode 100644
index 6aed0908..00000000
--- a/benchmark/utils/tatr.py
+++ /dev/null
@@ -1,115 +0,0 @@
-import torch
-from transformers import AutoModelForObjectDetection
-from surya.settings import settings
-import numpy as np
-
-
-class MaxResize(object):
- def __init__(self, max_size=800):
- self.max_size = max_size
-
- def __call__(self, image):
- width, height = image.size
- current_max_size = max(width, height)
- scale = self.max_size / current_max_size
- resized_image = image.resize((int(round(scale * width)), int(round(scale * height))))
-
- return resized_image
-
-
-def to_tensor(image):
- # Convert PIL Image to NumPy array
- np_image = np.array(image).astype(np.float32)
-
- # Rearrange dimensions to [C, H, W] format
- np_image = np_image.transpose((2, 0, 1))
-
- # Normalize to [0.0, 1.0]
- np_image /= 255.0
-
- return torch.from_numpy(np_image)
-
-
-def normalize(tensor, mean, std):
- for t, m, s in zip(tensor, mean, std):
- t.sub_(m).div_(s)
- return tensor
-
-
-def structure_transform(image):
- image = MaxResize(1000)(image)
- tensor = to_tensor(image)
- normalized_tensor = normalize(tensor, [0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
- return normalized_tensor
-
-
-def box_cxcywh_to_xyxy(x):
- x_c, y_c, w, h = x.unbind(-1)
- b = [(x_c - 0.5 * w), (y_c - 0.5 * h), (x_c + 0.5 * w), (y_c + 0.5 * h)]
- return torch.stack(b, dim=1)
-
-
-def rescale_bboxes(out_bbox, size):
- width, height = size
- boxes = box_cxcywh_to_xyxy(out_bbox)
- boxes = boxes * torch.tensor([width, height, width, height], dtype=torch.float32)
- return boxes
-
-
-def outputs_to_objects(outputs, img_sizes, id2label):
- m = outputs.logits.softmax(-1).max(-1)
- batch_labels = list(m.indices.detach().cpu().numpy())
- batch_scores = list(m.values.detach().cpu().numpy())
- batch_bboxes = outputs['pred_boxes'].detach().cpu()
-
- batch_objects = []
- for i in range(len(img_sizes)):
- pred_bboxes = [elem.tolist() for elem in rescale_bboxes(batch_bboxes[i], img_sizes[i])]
- pred_scores = batch_scores[i]
- pred_labels = batch_labels[i]
-
- objects = []
- for label, score, bbox in zip(pred_labels, pred_scores, pred_bboxes):
- class_label = id2label[int(label)]
- if not class_label == 'no object':
- objects.append({
- 'label': class_label,
- 'score': float(score),
- 'bbox': [float(elem) for elem in bbox]}
- )
-
- rows = []
- cols = []
- for cell in objects:
- if cell["label"] == "table column":
- cols.append(cell)
-
- if cell["label"] == "table row":
- rows.append(cell)
- batch_objects.append({
- "rows": rows,
- "cols": cols
- })
-
- return batch_objects
-
-
-def load_tatr():
- return AutoModelForObjectDetection.from_pretrained("microsoft/table-transformer-structure-recognition-v1.1-all").to(settings.TORCH_DEVICE_MODEL)
-
-
-def batch_inference_tatr(model, images, batch_size):
- device = model.device
- rows_cols = []
- for i in range(0, len(images), batch_size):
- batch_images = images[i:i + batch_size]
- pixel_values = torch.stack([structure_transform(img) for img in batch_images], dim=0).to(device)
-
- # forward pass
- with torch.no_grad():
- outputs = model(pixel_values)
-
- id2label = model.config.id2label
- id2label[len(model.config.id2label)] = "no object"
- rows_cols.extend(outputs_to_objects(outputs, [img.size for img in batch_images], id2label))
- return rows_cols
\ No newline at end of file
diff --git a/benchmark/utils/tesseract.py b/benchmark/utils/tesseract.py
deleted file mode 100644
index 3dc5c7ee..00000000
--- a/benchmark/utils/tesseract.py
+++ /dev/null
@@ -1,180 +0,0 @@
-from typing import List, Optional
-
-import numpy as np
-from tqdm import tqdm
-
-from surya.input.processing import slice_bboxes_from_image
-from surya.settings import settings
-import os
-from concurrent.futures import ProcessPoolExecutor
-from surya.recognition.languages import CODE_TO_LANGUAGE
-from surya.recognition import RecognitionPredictor
-from surya.detection import DetectionPredictor
-
-
-def surya_lang_to_tesseract(code: str) -> Optional[str]:
- lang_str = CODE_TO_LANGUAGE[code]
- try:
- tess_lang = TESS_LANGUAGE_TO_CODE[lang_str]
- except KeyError:
- return None
- return tess_lang
-
-
-def tesseract_ocr(img, bboxes, lang: str):
- import pytesseract
- line_imgs = slice_bboxes_from_image(img, bboxes)
- config = f'--tessdata-dir "{settings.TESSDATA_PREFIX}"'
- lines = []
- for line_img in line_imgs:
- line = pytesseract.image_to_string(line_img, lang=lang, config=config)
- lines.append(line)
- return lines
-
-
-def tesseract_ocr_parallel(imgs, bboxes, langs: List[str], cpus=None):
- tess_parallel_cores = min(len(imgs), RecognitionPredictor.get_batch_size())
- if not cpus:
- cpus = os.cpu_count()
- tess_parallel_cores = min(tess_parallel_cores, cpus)
-
- # Tesseract uses up to 4 processes per instance
- # Divide by 2 because tesseract doesn't seem to saturate all 4 cores with these small images
- tess_parallel = max(tess_parallel_cores // 2, 1)
-
- with ProcessPoolExecutor(max_workers=tess_parallel) as executor:
- tess_text = tqdm(executor.map(tesseract_ocr, imgs, bboxes, langs), total=len(imgs), desc="Running tesseract OCR")
- tess_text = list(tess_text)
- return tess_text
-
-
-def tesseract_bboxes(img):
- import pytesseract
- from pytesseract import Output
- arr_img = np.asarray(img, dtype=np.uint8)
- ocr = pytesseract.image_to_data(arr_img, output_type=Output.DICT)
-
- bboxes = []
- n_boxes = len(ocr['level'])
- for i in range(n_boxes):
- # It is possible to merge by line here with line number, but it gives bad results.
- _, x, y, w, h = ocr['text'][i], ocr['left'][i], ocr['top'][i], ocr['width'][i], ocr['height'][i]
- bbox = (x, y, x + w, y + h)
- bboxes.append(bbox)
-
- return bboxes
-
-
-def tesseract_parallel(imgs):
- # Tesseract uses 4 threads per instance
- tess_parallel_cores = min(len(imgs), DetectionPredictor.get_batch_size())
- cpus = os.cpu_count()
- tess_parallel_cores = min(tess_parallel_cores, cpus)
-
- # Tesseract uses 4 threads per instance
- tess_parallel = max(tess_parallel_cores // 4, 1)
-
- with ProcessPoolExecutor(max_workers=tess_parallel) as executor:
- tess_bboxes = tqdm(executor.map(tesseract_bboxes, imgs), total=len(imgs), desc="Running tesseract bbox detection")
- tess_bboxes = list(tess_bboxes)
- return tess_bboxes
-
-
-TESS_CODE_TO_LANGUAGE = {
- "afr": "Afrikaans",
- "amh": "Amharic",
- "ara": "Arabic",
- "asm": "Assamese",
- "aze": "Azerbaijani",
- "bel": "Belarusian",
- "ben": "Bengali",
- "bod": "Tibetan",
- "bos": "Bosnian",
- "bre": "Breton",
- "bul": "Bulgarian",
- "cat": "Catalan",
- "ceb": "Cebuano",
- "ces": "Czech",
- "chi_sim": "Chinese",
- "chr": "Cherokee",
- "cym": "Welsh",
- "dan": "Danish",
- "deu": "German",
- "dzo": "Dzongkha",
- "ell": "Greek",
- "eng": "English",
- "epo": "Esperanto",
- "est": "Estonian",
- "eus": "Basque",
- "fas": "Persian",
- "fin": "Finnish",
- "fra": "French",
- "fry": "Western Frisian",
- "guj": "Gujarati",
- "gla": "Scottish Gaelic",
- "gle": "Irish",
- "glg": "Galician",
- "heb": "Hebrew",
- "hin": "Hindi",
- "hrv": "Croatian",
- "hun": "Hungarian",
- "hye": "Armenian",
- "iku": "Inuktitut",
- "ind": "Indonesian",
- "isl": "Icelandic",
- "ita": "Italian",
- "jav": "Javanese",
- "jpn": "Japanese",
- "kan": "Kannada",
- "kat": "Georgian",
- "kaz": "Kazakh",
- "khm": "Khmer",
- "kir": "Kyrgyz",
- "kor": "Korean",
- "lao": "Lao",
- "lat": "Latin",
- "lav": "Latvian",
- "lit": "Lithuanian",
- "mal": "Malayalam",
- "mar": "Marathi",
- "mkd": "Macedonian",
- "mlt": "Maltese",
- "mon": "Mongolian",
- "msa": "Malay",
- "mya": "Burmese",
- "nep": "Nepali",
- "nld": "Dutch",
- "nor": "Norwegian",
- "ori": "Oriya",
- "pan": "Punjabi",
- "pol": "Polish",
- "por": "Portuguese",
- "pus": "Pashto",
- "ron": "Romanian",
- "rus": "Russian",
- "san": "Sanskrit",
- "sin": "Sinhala",
- "slk": "Slovak",
- "slv": "Slovenian",
- "snd": "Sindhi",
- "spa": "Spanish",
- "sqi": "Albanian",
- "srp": "Serbian",
- "swa": "Swahili",
- "swe": "Swedish",
- "syr": "Syriac",
- "tam": "Tamil",
- "tel": "Telugu",
- "tgk": "Tajik",
- "tha": "Thai",
- "tir": "Tigrinya",
- "tur": "Turkish",
- "uig": "Uyghur",
- "ukr": "Ukrainian",
- "urd": "Urdu",
- "uzb": "Uzbek",
- "vie": "Vietnamese",
- "yid": "Yiddish"
-}
-
-TESS_LANGUAGE_TO_CODE = {v:k for k,v in TESS_CODE_TO_LANGUAGE.items()}
diff --git a/benchmark/utils/textract.py b/benchmark/utils/textract.py
deleted file mode 100644
index 3edd275f..00000000
--- a/benchmark/utils/textract.py
+++ /dev/null
@@ -1,29 +0,0 @@
-import os
-from concurrent.futures import ThreadPoolExecutor
-from tqdm import tqdm
-import traceback
-
-from surya.input.processing import slice_bboxes_from_image
-from surya.recognition import RecognitionPredictor
-
-def textract_ocr(extractor, img):
- try:
- document = extractor.detect_document_text(file_source=img)
- return [line.text for line in document.lines]
- except:
- traceback.print_exc()
- return [None]
-
-def textract_ocr_parallel(imgs, cpus=None):
- from textractor import Textractor # Optional dependency
-
- extractor = Textractor(profile_name='default')
- parallel_cores = min(len(imgs), RecognitionPredictor().get_batch_size())
- if not cpus:
- cpus = os.cpu_count()
- parallel_cores = min(parallel_cores, cpus)
-
- with ThreadPoolExecutor(max_workers=parallel_cores) as executor:
- textract_text = tqdm(executor.map(textract_ocr, [extractor]*len(imgs), imgs), total=len(imgs), desc="Running textract OCR")
- textract_text = list(textract_text)
- return textract_text
\ No newline at end of file
diff --git a/benchmark/utils/verify_benchmark_scores.py b/benchmark/utils/verify_benchmark_scores.py
deleted file mode 100644
index d2c69404..00000000
--- a/benchmark/utils/verify_benchmark_scores.py
+++ /dev/null
@@ -1,73 +0,0 @@
-import json
-import click
-
-
-def verify_layout(data):
- scores = data["metrics"]
- for layout_type, metrics in scores.items():
- if layout_type == "List": # Skip lists since none appear early on
- continue
-
- if metrics["precision"] <= 0.6 or metrics["recall"] <= 0.6:
- raise ValueError("Scores do not meet the required threshold")
-
-
-def verify_det(data):
- scores = data["metrics"]["surya"]
- if scores["precision"] <= 0.9 or scores["recall"] <= 0.9:
- raise ValueError("Scores do not meet the required threshold")
-
-
-def verify_rec(data):
- scores = data["surya"]
- if scores["avg_score"] <= 0.9:
- raise ValueError("Scores do not meet the required threshold")
-
-
-def verify_order(data):
- score = data["mean_accuracy"]
- if score < 0.75:
- raise ValueError("Scores do not meet the required threshold")
-
-
-def verify_table_rec(data):
- row_score = data["surya"]["mean_row_iou"]
- col_score = data["surya"]["mean_col_iou"]
-
- if row_score < 0.75 or col_score < 0.75:
- raise ValueError("Scores do not meet the required threshold")
-
-
-def verify_texify(data):
- edit_dist = data["scores"]
- if edit_dist > 0.2:
- raise ValueError("Scores do not meet the required threshold")
-
-
-@click.command(help="Verify benchmark scores")
-@click.argument("file_path", type=str)
-@click.option(
- "--bench_type", type=str, help="Type of benchmark to verify", default="detection"
-)
-def main(file_path, bench_type):
- with open(file_path, "r") as file:
- data = json.load(file)
-
- if bench_type == "detection":
- verify_det(data)
- elif bench_type == "recognition":
- verify_rec(data)
- elif bench_type == "layout":
- verify_layout(data)
- elif bench_type == "ordering":
- verify_order(data)
- elif bench_type == "table_recognition":
- verify_table_rec(data)
- elif bench_type == "texify":
- verify_texify(data)
- else:
- raise ValueError("Invalid benchmark type")
-
-
-if __name__ == "__main__":
- main()
diff --git a/detect_layout.py b/detect_layout.py
deleted file mode 100644
index a087a837..00000000
--- a/detect_layout.py
+++ /dev/null
@@ -1,4 +0,0 @@
-from surya.scripts.detect_layout import detect_layout_cli
-
-if __name__ == "__main__":
- detect_layout_cli()
diff --git a/detect_text.py b/detect_text.py
deleted file mode 100644
index 9bbaa532..00000000
--- a/detect_text.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from surya.scripts.detect_text import detect_text_cli
-
-if __name__ == "__main__":
- detect_text_cli()
-
-
-
-
-
-
-
diff --git a/ocr_app.py b/ocr_app.py
deleted file mode 100644
index 98eedd72..00000000
--- a/ocr_app.py
+++ /dev/null
@@ -1,4 +0,0 @@
-from surya.scripts.run_streamlit_app import streamlit_app_cli
-
-if __name__ == "__main__":
- streamlit_app_cli()
\ No newline at end of file
diff --git a/ocr_latex.py b/ocr_latex.py
deleted file mode 100644
index ef774336..00000000
--- a/ocr_latex.py
+++ /dev/null
@@ -1,4 +0,0 @@
-from surya.scripts.ocr_latex import ocr_latex_cli
-
-if __name__ == "__main__":
- ocr_latex_cli()
diff --git a/ocr_text.py b/ocr_text.py
deleted file mode 100644
index aa5dd1b8..00000000
--- a/ocr_text.py
+++ /dev/null
@@ -1,4 +0,0 @@
-from surya.scripts.ocr_text import ocr_text_cli
-
-if __name__ == "__main__":
- ocr_text_cli()
diff --git a/poetry.lock b/poetry.lock
deleted file mode 100644
index 7e4aab75..00000000
--- a/poetry.lock
+++ /dev/null
@@ -1,5875 +0,0 @@
-# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand.
-
-[[package]]
-name = "absl-py"
-version = "2.3.1"
-description = "Abseil Python Common Libraries, see https://github.com/abseil/abseil-py."
-optional = false
-python-versions = ">=3.8"
-groups = ["xla"]
-files = [
- {file = "absl_py-2.3.1-py3-none-any.whl", hash = "sha256:eeecf07f0c2a93ace0772c92e596ace6d3d3996c042b2128459aaae2a76de11d"},
- {file = "absl_py-2.3.1.tar.gz", hash = "sha256:a97820526f7fbfd2ec1bce83f3f25e3a14840dac0d8e02a0b71cd75db3f77fc9"},
-]
-
-[[package]]
-name = "aiohappyeyeballs"
-version = "2.6.1"
-description = "Happy Eyeballs for asyncio"
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8"},
- {file = "aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558"},
-]
-
-[[package]]
-name = "aiohttp"
-version = "3.12.15"
-description = "Async http client/server framework (asyncio)"
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "aiohttp-3.12.15-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b6fc902bff74d9b1879ad55f5404153e2b33a82e72a95c89cec5eb6cc9e92fbc"},
- {file = "aiohttp-3.12.15-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:098e92835b8119b54c693f2f88a1dec690e20798ca5f5fe5f0520245253ee0af"},
- {file = "aiohttp-3.12.15-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:40b3fee496a47c3b4a39a731954c06f0bd9bd3e8258c059a4beb76ac23f8e421"},
- {file = "aiohttp-3.12.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ce13fcfb0bb2f259fb42106cdc63fa5515fb85b7e87177267d89a771a660b79"},
- {file = "aiohttp-3.12.15-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3beb14f053222b391bf9cf92ae82e0171067cc9c8f52453a0f1ec7c37df12a77"},
- {file = "aiohttp-3.12.15-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c39e87afe48aa3e814cac5f535bc6199180a53e38d3f51c5e2530f5aa4ec58c"},
- {file = "aiohttp-3.12.15-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5f1b4ce5bc528a6ee38dbf5f39bbf11dd127048726323b72b8e85769319ffc4"},
- {file = "aiohttp-3.12.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1004e67962efabbaf3f03b11b4c43b834081c9e3f9b32b16a7d97d4708a9abe6"},
- {file = "aiohttp-3.12.15-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8faa08fcc2e411f7ab91d1541d9d597d3a90e9004180edb2072238c085eac8c2"},
- {file = "aiohttp-3.12.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fe086edf38b2222328cdf89af0dde2439ee173b8ad7cb659b4e4c6f385b2be3d"},
- {file = "aiohttp-3.12.15-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:79b26fe467219add81d5e47b4a4ba0f2394e8b7c7c3198ed36609f9ba161aecb"},
- {file = "aiohttp-3.12.15-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b761bac1192ef24e16706d761aefcb581438b34b13a2f069a6d343ec8fb693a5"},
- {file = "aiohttp-3.12.15-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:e153e8adacfe2af562861b72f8bc47f8a5c08e010ac94eebbe33dc21d677cd5b"},
- {file = "aiohttp-3.12.15-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:fc49c4de44977aa8601a00edbf157e9a421f227aa7eb477d9e3df48343311065"},
- {file = "aiohttp-3.12.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:2776c7ec89c54a47029940177e75c8c07c29c66f73464784971d6a81904ce9d1"},
- {file = "aiohttp-3.12.15-cp310-cp310-win32.whl", hash = "sha256:2c7d81a277fa78b2203ab626ced1487420e8c11a8e373707ab72d189fcdad20a"},
- {file = "aiohttp-3.12.15-cp310-cp310-win_amd64.whl", hash = "sha256:83603f881e11f0f710f8e2327817c82e79431ec976448839f3cd05d7afe8f830"},
- {file = "aiohttp-3.12.15-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d3ce17ce0220383a0f9ea07175eeaa6aa13ae5a41f30bc61d84df17f0e9b1117"},
- {file = "aiohttp-3.12.15-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:010cc9bbd06db80fe234d9003f67e97a10fe003bfbedb40da7d71c1008eda0fe"},
- {file = "aiohttp-3.12.15-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3f9d7c55b41ed687b9d7165b17672340187f87a773c98236c987f08c858145a9"},
- {file = "aiohttp-3.12.15-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc4fbc61bb3548d3b482f9ac7ddd0f18c67e4225aaa4e8552b9f1ac7e6bda9e5"},
- {file = "aiohttp-3.12.15-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7fbc8a7c410bb3ad5d595bb7118147dfbb6449d862cc1125cf8867cb337e8728"},
- {file = "aiohttp-3.12.15-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:74dad41b3458dbb0511e760fb355bb0b6689e0630de8a22b1b62a98777136e16"},
- {file = "aiohttp-3.12.15-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b6f0af863cf17e6222b1735a756d664159e58855da99cfe965134a3ff63b0b0"},
- {file = "aiohttp-3.12.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b5b7fe4972d48a4da367043b8e023fb70a04d1490aa7d68800e465d1b97e493b"},
- {file = "aiohttp-3.12.15-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6443cca89553b7a5485331bc9bedb2342b08d073fa10b8c7d1c60579c4a7b9bd"},
- {file = "aiohttp-3.12.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6c5f40ec615e5264f44b4282ee27628cea221fcad52f27405b80abb346d9f3f8"},
- {file = "aiohttp-3.12.15-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:2abbb216a1d3a2fe86dbd2edce20cdc5e9ad0be6378455b05ec7f77361b3ab50"},
- {file = "aiohttp-3.12.15-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:db71ce547012a5420a39c1b744d485cfb823564d01d5d20805977f5ea1345676"},
- {file = "aiohttp-3.12.15-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ced339d7c9b5030abad5854aa5413a77565e5b6e6248ff927d3e174baf3badf7"},
- {file = "aiohttp-3.12.15-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:7c7dd29c7b5bda137464dc9bfc738d7ceea46ff70309859ffde8c022e9b08ba7"},
- {file = "aiohttp-3.12.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:421da6fd326460517873274875c6c5a18ff225b40da2616083c5a34a7570b685"},
- {file = "aiohttp-3.12.15-cp311-cp311-win32.whl", hash = "sha256:4420cf9d179ec8dfe4be10e7d0fe47d6d606485512ea2265b0d8c5113372771b"},
- {file = "aiohttp-3.12.15-cp311-cp311-win_amd64.whl", hash = "sha256:edd533a07da85baa4b423ee8839e3e91681c7bfa19b04260a469ee94b778bf6d"},
- {file = "aiohttp-3.12.15-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:802d3868f5776e28f7bf69d349c26fc0efadb81676d0afa88ed00d98a26340b7"},
- {file = "aiohttp-3.12.15-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f2800614cd560287be05e33a679638e586a2d7401f4ddf99e304d98878c29444"},
- {file = "aiohttp-3.12.15-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8466151554b593909d30a0a125d638b4e5f3836e5aecde85b66b80ded1cb5b0d"},
- {file = "aiohttp-3.12.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e5a495cb1be69dae4b08f35a6c4579c539e9b5706f606632102c0f855bcba7c"},
- {file = "aiohttp-3.12.15-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6404dfc8cdde35c69aaa489bb3542fb86ef215fc70277c892be8af540e5e21c0"},
- {file = "aiohttp-3.12.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3ead1c00f8521a5c9070fcb88f02967b1d8a0544e6d85c253f6968b785e1a2ab"},
- {file = "aiohttp-3.12.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6990ef617f14450bc6b34941dba4f12d5613cbf4e33805932f853fbd1cf18bfb"},
- {file = "aiohttp-3.12.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd736ed420f4db2b8148b52b46b88ed038d0354255f9a73196b7bbce3ea97545"},
- {file = "aiohttp-3.12.15-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c5092ce14361a73086b90c6efb3948ffa5be2f5b6fbcf52e8d8c8b8848bb97c"},
- {file = "aiohttp-3.12.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:aaa2234bb60c4dbf82893e934d8ee8dea30446f0647e024074237a56a08c01bd"},
- {file = "aiohttp-3.12.15-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:6d86a2fbdd14192e2f234a92d3b494dd4457e683ba07e5905a0b3ee25389ac9f"},
- {file = "aiohttp-3.12.15-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a041e7e2612041a6ddf1c6a33b883be6a421247c7afd47e885969ee4cc58bd8d"},
- {file = "aiohttp-3.12.15-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5015082477abeafad7203757ae44299a610e89ee82a1503e3d4184e6bafdd519"},
- {file = "aiohttp-3.12.15-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:56822ff5ddfd1b745534e658faba944012346184fbfe732e0d6134b744516eea"},
- {file = "aiohttp-3.12.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b2acbbfff69019d9014508c4ba0401822e8bae5a5fdc3b6814285b71231b60f3"},
- {file = "aiohttp-3.12.15-cp312-cp312-win32.whl", hash = "sha256:d849b0901b50f2185874b9a232f38e26b9b3d4810095a7572eacea939132d4e1"},
- {file = "aiohttp-3.12.15-cp312-cp312-win_amd64.whl", hash = "sha256:b390ef5f62bb508a9d67cb3bba9b8356e23b3996da7062f1a57ce1a79d2b3d34"},
- {file = "aiohttp-3.12.15-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:9f922ffd05034d439dde1c77a20461cf4a1b0831e6caa26151fe7aa8aaebc315"},
- {file = "aiohttp-3.12.15-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:2ee8a8ac39ce45f3e55663891d4b1d15598c157b4d494a4613e704c8b43112cd"},
- {file = "aiohttp-3.12.15-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3eae49032c29d356b94eee45a3f39fdf4b0814b397638c2f718e96cfadf4c4e4"},
- {file = "aiohttp-3.12.15-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b97752ff12cc12f46a9b20327104448042fce5c33a624f88c18f66f9368091c7"},
- {file = "aiohttp-3.12.15-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:894261472691d6fe76ebb7fcf2e5870a2ac284c7406ddc95823c8598a1390f0d"},
- {file = "aiohttp-3.12.15-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5fa5d9eb82ce98959fc1031c28198b431b4d9396894f385cb63f1e2f3f20ca6b"},
- {file = "aiohttp-3.12.15-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0fa751efb11a541f57db59c1dd821bec09031e01452b2b6217319b3a1f34f3d"},
- {file = "aiohttp-3.12.15-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5346b93e62ab51ee2a9d68e8f73c7cf96ffb73568a23e683f931e52450e4148d"},
- {file = "aiohttp-3.12.15-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:049ec0360f939cd164ecbfd2873eaa432613d5e77d6b04535e3d1fbae5a9e645"},
- {file = "aiohttp-3.12.15-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b52dcf013b57464b6d1e51b627adfd69a8053e84b7103a7cd49c030f9ca44461"},
- {file = "aiohttp-3.12.15-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:9b2af240143dd2765e0fb661fd0361a1b469cab235039ea57663cda087250ea9"},
- {file = "aiohttp-3.12.15-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ac77f709a2cde2cc71257ab2d8c74dd157c67a0558a0d2799d5d571b4c63d44d"},
- {file = "aiohttp-3.12.15-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:47f6b962246f0a774fbd3b6b7be25d59b06fdb2f164cf2513097998fc6a29693"},
- {file = "aiohttp-3.12.15-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:760fb7db442f284996e39cf9915a94492e1896baac44f06ae551974907922b64"},
- {file = "aiohttp-3.12.15-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ad702e57dc385cae679c39d318def49aef754455f237499d5b99bea4ef582e51"},
- {file = "aiohttp-3.12.15-cp313-cp313-win32.whl", hash = "sha256:f813c3e9032331024de2eb2e32a88d86afb69291fbc37a3a3ae81cc9917fb3d0"},
- {file = "aiohttp-3.12.15-cp313-cp313-win_amd64.whl", hash = "sha256:1a649001580bdb37c6fdb1bebbd7e3bc688e8ec2b5c6f52edbb664662b17dc84"},
- {file = "aiohttp-3.12.15-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:691d203c2bdf4f4637792efbbcdcd157ae11e55eaeb5e9c360c1206fb03d4d98"},
- {file = "aiohttp-3.12.15-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8e995e1abc4ed2a454c731385bf4082be06f875822adc4c6d9eaadf96e20d406"},
- {file = "aiohttp-3.12.15-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:bd44d5936ab3193c617bfd6c9a7d8d1085a8dc8c3f44d5f1dcf554d17d04cf7d"},
- {file = "aiohttp-3.12.15-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46749be6e89cd78d6068cdf7da51dbcfa4321147ab8e4116ee6678d9a056a0cf"},
- {file = "aiohttp-3.12.15-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0c643f4d75adea39e92c0f01b3fb83d57abdec8c9279b3078b68a3a52b3933b6"},
- {file = "aiohttp-3.12.15-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0a23918fedc05806966a2438489dcffccbdf83e921a1170773b6178d04ade142"},
- {file = "aiohttp-3.12.15-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:74bdd8c864b36c3673741023343565d95bfbd778ffe1eb4d412c135a28a8dc89"},
- {file = "aiohttp-3.12.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a146708808c9b7a988a4af3821379e379e0f0e5e466ca31a73dbdd0325b0263"},
- {file = "aiohttp-3.12.15-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7011a70b56facde58d6d26da4fec3280cc8e2a78c714c96b7a01a87930a9530"},
- {file = "aiohttp-3.12.15-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3bdd6e17e16e1dbd3db74d7f989e8af29c4d2e025f9828e6ef45fbdee158ec75"},
- {file = "aiohttp-3.12.15-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:57d16590a351dfc914670bd72530fd78344b885a00b250e992faea565b7fdc05"},
- {file = "aiohttp-3.12.15-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:bc9a0f6569ff990e0bbd75506c8d8fe7214c8f6579cca32f0546e54372a3bb54"},
- {file = "aiohttp-3.12.15-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:536ad7234747a37e50e7b6794ea868833d5220b49c92806ae2d7e8a9d6b5de02"},
- {file = "aiohttp-3.12.15-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f0adb4177fa748072546fb650d9bd7398caaf0e15b370ed3317280b13f4083b0"},
- {file = "aiohttp-3.12.15-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:14954a2988feae3987f1eb49c706bff39947605f4b6fa4027c1d75743723eb09"},
- {file = "aiohttp-3.12.15-cp39-cp39-win32.whl", hash = "sha256:b784d6ed757f27574dca1c336f968f4e81130b27595e458e69457e6878251f5d"},
- {file = "aiohttp-3.12.15-cp39-cp39-win_amd64.whl", hash = "sha256:86ceded4e78a992f835209e236617bffae649371c4a50d5e5a3987f237db84b8"},
- {file = "aiohttp-3.12.15.tar.gz", hash = "sha256:4fc61385e9c98d72fcdf47e6dd81833f47b2f77c114c29cd64a361be57a763a2"},
-]
-
-[package.dependencies]
-aiohappyeyeballs = ">=2.5.0"
-aiosignal = ">=1.4.0"
-async-timeout = {version = ">=4.0,<6.0", markers = "python_version < \"3.11\""}
-attrs = ">=17.3.0"
-frozenlist = ">=1.1.1"
-multidict = ">=4.5,<7.0"
-propcache = ">=0.2.0"
-yarl = ">=1.17.0,<2.0"
-
-[package.extras]
-speedups = ["Brotli ; platform_python_implementation == \"CPython\"", "aiodns (>=3.3.0)", "brotlicffi ; platform_python_implementation != \"CPython\""]
-
-[[package]]
-name = "aiosignal"
-version = "1.4.0"
-description = "aiosignal: a list of registered asynchronous callbacks"
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e"},
- {file = "aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7"},
-]
-
-[package.dependencies]
-frozenlist = ">=1.1.0"
-typing-extensions = {version = ">=4.2", markers = "python_version < \"3.13\""}
-
-[[package]]
-name = "altair"
-version = "5.5.0"
-description = "Vega-Altair: A declarative statistical visualization library for Python."
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "altair-5.5.0-py3-none-any.whl", hash = "sha256:91a310b926508d560fe0148d02a194f38b824122641ef528113d029fcd129f8c"},
- {file = "altair-5.5.0.tar.gz", hash = "sha256:d960ebe6178c56de3855a68c47b516be38640b73fb3b5111c2a9ca90546dd73d"},
-]
-
-[package.dependencies]
-jinja2 = "*"
-jsonschema = ">=3.0"
-narwhals = ">=1.14.2"
-packaging = "*"
-typing-extensions = {version = ">=4.10.0", markers = "python_version < \"3.14\""}
-
-[package.extras]
-all = ["altair-tiles (>=0.3.0)", "anywidget (>=0.9.0)", "numpy", "pandas (>=1.1.3)", "pyarrow (>=11)", "vega-datasets (>=0.9.0)", "vegafusion[embed] (>=1.6.6)", "vl-convert-python (>=1.7.0)"]
-dev = ["duckdb (>=1.0)", "geopandas", "hatch (>=1.13.0)", "ipython[kernel]", "mistune", "mypy", "pandas (>=1.1.3)", "pandas-stubs", "polars (>=0.20.3)", "pyarrow-stubs", "pytest", "pytest-cov", "pytest-xdist[psutil] (>=3.5,<4.0)", "ruff (>=0.6.0)", "types-jsonschema", "types-setuptools"]
-doc = ["docutils", "jinja2", "myst-parser", "numpydoc", "pillow (>=9,<10)", "pydata-sphinx-theme (>=0.14.1)", "scipy", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinxext-altair"]
-save = ["vl-convert-python (>=1.7.0)"]
-
-[[package]]
-name = "annotated-types"
-version = "0.7.0"
-description = "Reusable constraint types to use with typing.Annotated"
-optional = false
-python-versions = ">=3.8"
-groups = ["main", "dev"]
-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"},
-]
-
-[[package]]
-name = "anyio"
-version = "4.10.0"
-description = "High-level concurrency and networking framework on top of asyncio or Trio"
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "anyio-4.10.0-py3-none-any.whl", hash = "sha256:60e474ac86736bbfd6f210f7a61218939c318f43f9972497381f1c5e930ed3d1"},
- {file = "anyio-4.10.0.tar.gz", hash = "sha256:3f3fae35c96039744587aa5b8371e7e8e603c0702999535961dd336026973ba6"},
-]
-
-[package.dependencies]
-exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""}
-idna = ">=2.8"
-sniffio = ">=1.1"
-typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""}
-
-[package.extras]
-trio = ["trio (>=0.26.1)"]
-
-[[package]]
-name = "appnope"
-version = "0.1.4"
-description = "Disable App Nap on macOS >= 10.9"
-optional = false
-python-versions = ">=3.6"
-groups = ["dev"]
-markers = "platform_system == \"Darwin\""
-files = [
- {file = "appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c"},
- {file = "appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee"},
-]
-
-[[package]]
-name = "argon2-cffi"
-version = "25.1.0"
-description = "Argon2 for Python"
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "argon2_cffi-25.1.0-py3-none-any.whl", hash = "sha256:fdc8b074db390fccb6eb4a3604ae7231f219aa669a2652e0f20e16ba513d5741"},
- {file = "argon2_cffi-25.1.0.tar.gz", hash = "sha256:694ae5cc8a42f4c4e2bf2ca0e64e51e23a040c6a517a85074683d3959e1346c1"},
-]
-
-[package.dependencies]
-argon2-cffi-bindings = "*"
-
-[[package]]
-name = "argon2-cffi-bindings"
-version = "21.2.0"
-description = "Low-level CFFI bindings for Argon2"
-optional = false
-python-versions = ">=3.6"
-groups = ["dev"]
-markers = "python_version >= \"3.14\""
-files = [
- {file = "argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3"},
- {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367"},
- {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d"},
- {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae"},
- {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c"},
- {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86"},
- {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f"},
- {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e"},
- {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082"},
- {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f"},
- {file = "argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93"},
- {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3e385d1c39c520c08b53d63300c3ecc28622f076f4c2b0e6d7e796e9f6502194"},
- {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3e3cc67fdb7d82c4718f19b4e7a87123caf8a93fde7e23cf66ac0337d3cb3f"},
- {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a22ad9800121b71099d0fb0a65323810a15f2e292f2ba450810a7316e128ee5"},
- {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9f8b450ed0547e3d473fdc8612083fd08dd2120d6ac8f73828df9b7d45bb351"},
- {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:93f9bf70084f97245ba10ee36575f0c3f1e7d7724d67d8e5b08e61787c320ed7"},
- {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3b9ef65804859d335dc6b31582cad2c5166f0c3e7975f324d9ffaa34ee7e6583"},
- {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4966ef5848d820776f5f562a7d45fdd70c2f330c961d0d745b784034bd9f48d"},
- {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ef543a89dee4db46a1a6e206cd015360e5a75822f76df533845c3cbaf72670"},
- {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2937d286e2ad0cc79a7087d3c272832865f779430e0cc2b4f3718d3159b0cb"},
- {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5e00316dabdaea0b2dd82d141cc66889ced0cdcbfa599e8b471cf22c620c329a"},
-]
-
-[package.dependencies]
-cffi = ">=1.0.1"
-
-[package.extras]
-dev = ["cogapp", "pre-commit", "pytest", "wheel"]
-tests = ["pytest"]
-
-[[package]]
-name = "argon2-cffi-bindings"
-version = "25.1.0"
-description = "Low-level CFFI bindings for Argon2"
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-markers = "python_version < \"3.14\""
-files = [
- {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:3d3f05610594151994ca9ccb3c771115bdb4daef161976a266f0dd8aa9996b8f"},
- {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8b8efee945193e667a396cbc7b4fb7d357297d6234d30a489905d96caabde56b"},
- {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3c6702abc36bf3ccba3f802b799505def420a1b7039862014a65db3205967f5a"},
- {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a1c70058c6ab1e352304ac7e3b52554daadacd8d453c1752e547c76e9c99ac44"},
- {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e2fd3bfbff3c5d74fef31a722f729bf93500910db650c925c2d6ef879a7e51cb"},
- {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4f9665de60b1b0e99bcd6be4f17d90339698ce954cfd8d9cf4f91c995165a92"},
- {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ba92837e4a9aa6a508c8d2d7883ed5a8f6c308c89a4790e1e447a220deb79a85"},
- {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-win32.whl", hash = "sha256:84a461d4d84ae1295871329b346a97f68eade8c53b6ed9a7ca2d7467f3c8ff6f"},
- {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b55aec3565b65f56455eebc9b9f34130440404f27fe21c3b375bf1ea4d8fbae6"},
- {file = "argon2_cffi_bindings-25.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:87c33a52407e4c41f3b70a9c2d3f6056d88b10dad7695be708c5021673f55623"},
- {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:aecba1723ae35330a008418a91ea6cfcedf6d31e5fbaa056a166462ff066d500"},
- {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:2630b6240b495dfab90aebe159ff784d08ea999aa4b0d17efa734055a07d2f44"},
- {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:7aef0c91e2c0fbca6fc68e7555aa60ef7008a739cbe045541e438373bc54d2b0"},
- {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e021e87faa76ae0d413b619fe2b65ab9a037f24c60a1e6cc43457ae20de6dc6"},
- {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d3e924cfc503018a714f94a49a149fdc0b644eaead5d1f089330399134fa028a"},
- {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c87b72589133f0346a1cb8d5ecca4b933e3c9b64656c9d175270a000e73b288d"},
- {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:1db89609c06afa1a214a69a462ea741cf735b29a57530478c06eb81dd403de99"},
- {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-win32.whl", hash = "sha256:473bcb5f82924b1becbb637b63303ec8d10e84c8d241119419897a26116515d2"},
- {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-win_amd64.whl", hash = "sha256:a98cd7d17e9f7ce244c0803cad3c23a7d379c301ba618a5fa76a67d116618b98"},
- {file = "argon2_cffi_bindings-25.1.0-cp39-abi3-win_arm64.whl", hash = "sha256:b0fdbcf513833809c882823f98dc2f931cf659d9a1429616ac3adebb49f5db94"},
- {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6dca33a9859abf613e22733131fc9194091c1fa7cb3e131c143056b4856aa47e"},
- {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:21378b40e1b8d1655dd5310c84a40fc19a9aa5e6366e835ceb8576bf0fea716d"},
- {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d588dec224e2a83edbdc785a5e6f3c6cd736f46bfd4b441bbb5aa1f5085e584"},
- {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5acb4e41090d53f17ca1110c3427f0a130f944b896fc8c83973219c97f57b690"},
- {file = "argon2_cffi_bindings-25.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:da0c79c23a63723aa5d782250fbf51b768abca630285262fb5144ba5ae01e520"},
- {file = "argon2_cffi_bindings-25.1.0.tar.gz", hash = "sha256:b957f3e6ea4d55d820e40ff76f450952807013d361a65d7f28acc0acbf29229d"},
-]
-
-[package.dependencies]
-cffi = {version = ">=1.0.1", markers = "python_version < \"3.14\""}
-
-[[package]]
-name = "arrow"
-version = "1.3.0"
-description = "Better dates & times for Python"
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "arrow-1.3.0-py3-none-any.whl", hash = "sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80"},
- {file = "arrow-1.3.0.tar.gz", hash = "sha256:d4540617648cb5f895730f1ad8c82a65f2dad0166f57b75f3ca54759c4d67a85"},
-]
-
-[package.dependencies]
-python-dateutil = ">=2.7.0"
-types-python-dateutil = ">=2.8.10"
-
-[package.extras]
-doc = ["doc8", "sphinx (>=7.0.0)", "sphinx-autobuild", "sphinx-autodoc-typehints", "sphinx_rtd_theme (>=1.3.0)"]
-test = ["dateparser (==1.*)", "pre-commit", "pytest", "pytest-cov", "pytest-mock", "pytz (==2021.1)", "simplejson (==3.*)"]
-
-[[package]]
-name = "asttokens"
-version = "3.0.0"
-description = "Annotate AST trees with source code positions"
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "asttokens-3.0.0-py3-none-any.whl", hash = "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2"},
- {file = "asttokens-3.0.0.tar.gz", hash = "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7"},
-]
-
-[package.extras]
-astroid = ["astroid (>=2,<4)"]
-test = ["astroid (>=2,<4)", "pytest", "pytest-cov", "pytest-xdist"]
-
-[[package]]
-name = "async-lru"
-version = "2.0.5"
-description = "Simple LRU cache for asyncio"
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "async_lru-2.0.5-py3-none-any.whl", hash = "sha256:ab95404d8d2605310d345932697371a5f40def0487c03d6d0ad9138de52c9943"},
- {file = "async_lru-2.0.5.tar.gz", hash = "sha256:481d52ccdd27275f42c43a928b4a50c3bfb2d67af4e78b170e3e0bb39c66e5bb"},
-]
-
-[package.dependencies]
-typing_extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""}
-
-[[package]]
-name = "async-timeout"
-version = "5.0.1"
-description = "Timeout context manager for asyncio programs"
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-markers = "python_version == \"3.10\""
-files = [
- {file = "async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c"},
- {file = "async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3"},
-]
-
-[[package]]
-name = "attrs"
-version = "25.3.0"
-description = "Classes Without Boilerplate"
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "attrs-25.3.0-py3-none-any.whl", hash = "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3"},
- {file = "attrs-25.3.0.tar.gz", hash = "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b"},
-]
-
-[package.extras]
-benchmark = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
-cov = ["cloudpickle ; platform_python_implementation == \"CPython\"", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
-dev = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pre-commit-uv", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
-docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"]
-tests = ["cloudpickle ; platform_python_implementation == \"CPython\"", "hypothesis", "mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-xdist[psutil]"]
-tests-mypy = ["mypy (>=1.11.1) ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\"", "pytest-mypy-plugins ; platform_python_implementation == \"CPython\" and python_version >= \"3.10\""]
-
-[[package]]
-name = "babel"
-version = "2.17.0"
-description = "Internationalization utilities"
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2"},
- {file = "babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d"},
-]
-
-[package.extras]
-dev = ["backports.zoneinfo ; python_version < \"3.9\"", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata ; sys_platform == \"win32\""]
-
-[[package]]
-name = "beautifulsoup4"
-version = "4.13.5"
-description = "Screen-scraping library"
-optional = false
-python-versions = ">=3.7.0"
-groups = ["dev"]
-files = [
- {file = "beautifulsoup4-4.13.5-py3-none-any.whl", hash = "sha256:642085eaa22233aceadff9c69651bc51e8bf3f874fb6d7104ece2beb24b47c4a"},
- {file = "beautifulsoup4-4.13.5.tar.gz", hash = "sha256:5e70131382930e7c3de33450a2f54a63d5e4b19386eab43a5b34d594268f3695"},
-]
-
-[package.dependencies]
-soupsieve = ">1.2"
-typing-extensions = ">=4.0.0"
-
-[package.extras]
-cchardet = ["cchardet"]
-chardet = ["chardet"]
-charset-normalizer = ["charset-normalizer"]
-html5lib = ["html5lib"]
-lxml = ["lxml"]
-
-[[package]]
-name = "bleach"
-version = "6.2.0"
-description = "An easy safelist-based HTML-sanitizing tool."
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "bleach-6.2.0-py3-none-any.whl", hash = "sha256:117d9c6097a7c3d22fd578fcd8d35ff1e125df6736f554da4e432fdd63f31e5e"},
- {file = "bleach-6.2.0.tar.gz", hash = "sha256:123e894118b8a599fd80d3ec1a6d4cc7ce4e5882b1317a7e1ba69b56e95f991f"},
-]
-
-[package.dependencies]
-tinycss2 = {version = ">=1.1.0,<1.5", optional = true, markers = "extra == \"css\""}
-webencodings = "*"
-
-[package.extras]
-css = ["tinycss2 (>=1.1.0,<1.5)"]
-
-[[package]]
-name = "blinker"
-version = "1.9.0"
-description = "Fast, simple object-to-object and broadcast signaling"
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc"},
- {file = "blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf"},
-]
-
-[[package]]
-name = "cachetools"
-version = "6.2.0"
-description = "Extensible memoizing collections and decorators"
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "cachetools-6.2.0-py3-none-any.whl", hash = "sha256:1c76a8960c0041fcc21097e357f882197c79da0dbff766e7317890a65d7d8ba6"},
- {file = "cachetools-6.2.0.tar.gz", hash = "sha256:38b328c0889450f05f5e120f56ab68c8abaf424e1275522b138ffc93253f7e32"},
-]
-
-[[package]]
-name = "certifi"
-version = "2025.8.3"
-description = "Python package for providing Mozilla's CA Bundle."
-optional = false
-python-versions = ">=3.7"
-groups = ["main", "dev", "xla"]
-files = [
- {file = "certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5"},
- {file = "certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407"},
-]
-
-[[package]]
-name = "cffi"
-version = "1.17.1"
-description = "Foreign Function Interface for Python calling C code."
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-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"},
- {file = "cffi-1.17.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382"},
- {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702"},
- {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3"},
- {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6"},
- {file = "cffi-1.17.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17"},
- {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8"},
- {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e"},
- {file = "cffi-1.17.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be"},
- {file = "cffi-1.17.1-cp310-cp310-win32.whl", hash = "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c"},
- {file = "cffi-1.17.1-cp310-cp310-win_amd64.whl", hash = "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15"},
- {file = "cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401"},
- {file = "cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf"},
- {file = "cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4"},
- {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41"},
- {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1"},
- {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6"},
- {file = "cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d"},
- {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6"},
- {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f"},
- {file = "cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b"},
- {file = "cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655"},
- {file = "cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0"},
- {file = "cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4"},
- {file = "cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c"},
- {file = "cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36"},
- {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5"},
- {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff"},
- {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99"},
- {file = "cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93"},
- {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3"},
- {file = "cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8"},
- {file = "cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65"},
- {file = "cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903"},
- {file = "cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e"},
- {file = "cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2"},
- {file = "cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3"},
- {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683"},
- {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5"},
- {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4"},
- {file = "cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd"},
- {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed"},
- {file = "cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9"},
- {file = "cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d"},
- {file = "cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a"},
- {file = "cffi-1.17.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b"},
- {file = "cffi-1.17.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964"},
- {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9"},
- {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc"},
- {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c"},
- {file = "cffi-1.17.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1"},
- {file = "cffi-1.17.1-cp38-cp38-win32.whl", hash = "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8"},
- {file = "cffi-1.17.1-cp38-cp38-win_amd64.whl", hash = "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1"},
- {file = "cffi-1.17.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16"},
- {file = "cffi-1.17.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36"},
- {file = "cffi-1.17.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8"},
- {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576"},
- {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87"},
- {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0"},
- {file = "cffi-1.17.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3"},
- {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595"},
- {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a"},
- {file = "cffi-1.17.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e"},
- {file = "cffi-1.17.1-cp39-cp39-win32.whl", hash = "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7"},
- {file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"},
- {file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"},
-]
-
-[package.dependencies]
-pycparser = "*"
-
-[[package]]
-name = "cfgv"
-version = "3.4.0"
-description = "Validate configuration and produce human readable error messages."
-optional = false
-python-versions = ">=3.8"
-groups = ["main"]
-files = [
- {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"},
- {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"},
-]
-
-[[package]]
-name = "charset-normalizer"
-version = "3.4.3"
-description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
-optional = false
-python-versions = ">=3.7"
-groups = ["main", "dev", "xla"]
-files = [
- {file = "charset_normalizer-3.4.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fb7f67a1bfa6e40b438170ebdc8158b78dc465a5a67b6dde178a46987b244a72"},
- {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc9370a2da1ac13f0153780040f465839e6cccb4a1e44810124b4e22483c93fe"},
- {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:07a0eae9e2787b586e129fdcbe1af6997f8d0e5abaa0bc98c0e20e124d67e601"},
- {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:74d77e25adda8581ffc1c720f1c81ca082921329452eba58b16233ab1842141c"},
- {file = "charset_normalizer-3.4.3-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0e909868420b7049dafd3a31d45125b31143eec59235311fc4c57ea26a4acd2"},
- {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c6f162aabe9a91a309510d74eeb6507fab5fff92337a15acbe77753d88d9dcf0"},
- {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4ca4c094de7771a98d7fbd67d9e5dbf1eb73efa4f744a730437d8a3a5cf994f0"},
- {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:02425242e96bcf29a49711b0ca9f37e451da7c70562bc10e8ed992a5a7a25cc0"},
- {file = "charset_normalizer-3.4.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:78deba4d8f9590fe4dae384aeff04082510a709957e968753ff3c48399f6f92a"},
- {file = "charset_normalizer-3.4.3-cp310-cp310-win32.whl", hash = "sha256:d79c198e27580c8e958906f803e63cddb77653731be08851c7df0b1a14a8fc0f"},
- {file = "charset_normalizer-3.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:c6e490913a46fa054e03699c70019ab869e990270597018cef1d8562132c2669"},
- {file = "charset_normalizer-3.4.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b256ee2e749283ef3ddcff51a675ff43798d92d746d1a6e4631bf8c707d22d0b"},
- {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13faeacfe61784e2559e690fc53fa4c5ae97c6fcedb8eb6fb8d0a15b475d2c64"},
- {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:00237675befef519d9af72169d8604a067d92755e84fe76492fef5441db05b91"},
- {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:585f3b2a80fbd26b048a0be90c5aae8f06605d3c92615911c3a2b03a8a3b796f"},
- {file = "charset_normalizer-3.4.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e78314bdc32fa80696f72fa16dc61168fda4d6a0c014e0380f9d02f0e5d8a07"},
- {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:96b2b3d1a83ad55310de8c7b4a2d04d9277d5591f40761274856635acc5fcb30"},
- {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:939578d9d8fd4299220161fdd76e86c6a251987476f5243e8864a7844476ba14"},
- {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fd10de089bcdcd1be95a2f73dbe6254798ec1bda9f450d5828c96f93e2536b9c"},
- {file = "charset_normalizer-3.4.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1e8ac75d72fa3775e0b7cb7e4629cec13b7514d928d15ef8ea06bca03ef01cae"},
- {file = "charset_normalizer-3.4.3-cp311-cp311-win32.whl", hash = "sha256:6cf8fd4c04756b6b60146d98cd8a77d0cdae0e1ca20329da2ac85eed779b6849"},
- {file = "charset_normalizer-3.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:31a9a6f775f9bcd865d88ee350f0ffb0e25936a7f930ca98995c05abf1faf21c"},
- {file = "charset_normalizer-3.4.3-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e28e334d3ff134e88989d90ba04b47d84382a828c061d0d1027b1b12a62b39b1"},
- {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cacf8f7297b0c4fcb74227692ca46b4a5852f8f4f24b3c766dd94a1075c4884"},
- {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c6fd51128a41297f5409deab284fecbe5305ebd7e5a1f959bee1c054622b7018"},
- {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3cfb2aad70f2c6debfbcb717f23b7eb55febc0bb23dcffc0f076009da10c6392"},
- {file = "charset_normalizer-3.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1606f4a55c0fd363d754049cdf400175ee96c992b1f8018b993941f221221c5f"},
- {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:027b776c26d38b7f15b26a5da1044f376455fb3766df8fc38563b4efbc515154"},
- {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:42e5088973e56e31e4fa58eb6bd709e42fc03799c11c42929592889a2e54c491"},
- {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cc34f233c9e71701040d772aa7490318673aa7164a0efe3172b2981218c26d93"},
- {file = "charset_normalizer-3.4.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320e8e66157cc4e247d9ddca8e21f427efc7a04bbd0ac8a9faf56583fa543f9f"},
- {file = "charset_normalizer-3.4.3-cp312-cp312-win32.whl", hash = "sha256:fb6fecfd65564f208cbf0fba07f107fb661bcd1a7c389edbced3f7a493f70e37"},
- {file = "charset_normalizer-3.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:86df271bf921c2ee3818f0522e9a5b8092ca2ad8b065ece5d7d9d0e9f4849bcc"},
- {file = "charset_normalizer-3.4.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:14c2a87c65b351109f6abfc424cab3927b3bdece6f706e4d12faaf3d52ee5efe"},
- {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41d1fc408ff5fdfb910200ec0e74abc40387bccb3252f3f27c0676731df2b2c8"},
- {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1bb60174149316da1c35fa5233681f7c0f9f514509b8e399ab70fea5f17e45c9"},
- {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:30d006f98569de3459c2fc1f2acde170b7b2bd265dc1943e87e1a4efe1b67c31"},
- {file = "charset_normalizer-3.4.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:416175faf02e4b0810f1f38bcb54682878a4af94059a1cd63b8747244420801f"},
- {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6aab0f181c486f973bc7262a97f5aca3ee7e1437011ef0c2ec04b5a11d16c927"},
- {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:fdabf8315679312cfa71302f9bd509ded4f2f263fb5b765cf1433b39106c3cc9"},
- {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:bd28b817ea8c70215401f657edef3a8aa83c29d447fb0b622c35403780ba11d5"},
- {file = "charset_normalizer-3.4.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:18343b2d246dc6761a249ba1fb13f9ee9a2bcd95decc767319506056ea4ad4dc"},
- {file = "charset_normalizer-3.4.3-cp313-cp313-win32.whl", hash = "sha256:6fb70de56f1859a3f71261cbe41005f56a7842cc348d3aeb26237560bfa5e0ce"},
- {file = "charset_normalizer-3.4.3-cp313-cp313-win_amd64.whl", hash = "sha256:cf1ebb7d78e1ad8ec2a8c4732c7be2e736f6e5123a4146c5b89c9d1f585f8cef"},
- {file = "charset_normalizer-3.4.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3cd35b7e8aedeb9e34c41385fda4f73ba609e561faedfae0a9e75e44ac558a15"},
- {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b89bc04de1d83006373429975f8ef9e7932534b8cc9ca582e4db7d20d91816db"},
- {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2001a39612b241dae17b4687898843f254f8748b796a2e16f1051a17078d991d"},
- {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8dcfc373f888e4fb39a7bc57e93e3b845e7f462dacc008d9749568b1c4ece096"},
- {file = "charset_normalizer-3.4.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:18b97b8404387b96cdbd30ad660f6407799126d26a39ca65729162fd810a99aa"},
- {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ccf600859c183d70eb47e05a44cd80a4ce77394d1ac0f79dbd2dd90a69a3a049"},
- {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:53cd68b185d98dde4ad8990e56a58dea83a4162161b1ea9272e5c9182ce415e0"},
- {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:30a96e1e1f865f78b030d65241c1ee850cdf422d869e9028e2fc1d5e4db73b92"},
- {file = "charset_normalizer-3.4.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d716a916938e03231e86e43782ca7878fb602a125a91e7acb8b5112e2e96ac16"},
- {file = "charset_normalizer-3.4.3-cp314-cp314-win32.whl", hash = "sha256:c6dbd0ccdda3a2ba7c2ecd9d77b37f3b5831687d8dc1b6ca5f56a4880cc7b7ce"},
- {file = "charset_normalizer-3.4.3-cp314-cp314-win_amd64.whl", hash = "sha256:73dc19b562516fc9bcf6e5d6e596df0b4eb98d87e4f79f3ae71840e6ed21361c"},
- {file = "charset_normalizer-3.4.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0f2be7e0cf7754b9a30eb01f4295cc3d4358a479843b31f328afd210e2c7598c"},
- {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c60e092517a73c632ec38e290eba714e9627abe9d301c8c8a12ec32c314a2a4b"},
- {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:252098c8c7a873e17dd696ed98bbe91dbacd571da4b87df3736768efa7a792e4"},
- {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3653fad4fe3ed447a596ae8638b437f827234f01a8cd801842e43f3d0a6b281b"},
- {file = "charset_normalizer-3.4.3-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8999f965f922ae054125286faf9f11bc6932184b93011d138925a1773830bbe9"},
- {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d95bfb53c211b57198bb91c46dd5a2d8018b3af446583aab40074bf7988401cb"},
- {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:5b413b0b1bfd94dbf4023ad6945889f374cd24e3f62de58d6bb102c4d9ae534a"},
- {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:b5e3b2d152e74e100a9e9573837aba24aab611d39428ded46f4e4022ea7d1942"},
- {file = "charset_normalizer-3.4.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:a2d08ac246bb48479170408d6c19f6385fa743e7157d716e144cad849b2dd94b"},
- {file = "charset_normalizer-3.4.3-cp38-cp38-win32.whl", hash = "sha256:ec557499516fc90fd374bf2e32349a2887a876fbf162c160e3c01b6849eaf557"},
- {file = "charset_normalizer-3.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:5d8d01eac18c423815ed4f4a2ec3b439d654e55ee4ad610e153cf02faf67ea40"},
- {file = "charset_normalizer-3.4.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:70bfc5f2c318afece2f5838ea5e4c3febada0be750fcf4775641052bbba14d05"},
- {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:23b6b24d74478dc833444cbd927c338349d6ae852ba53a0d02a2de1fce45b96e"},
- {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:34a7f768e3f985abdb42841e20e17b330ad3aaf4bb7e7aeeb73db2e70f077b99"},
- {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb731e5deb0c7ef82d698b0f4c5bb724633ee2a489401594c5c88b02e6cb15f7"},
- {file = "charset_normalizer-3.4.3-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:257f26fed7d7ff59921b78244f3cd93ed2af1800ff048c33f624c87475819dd7"},
- {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1ef99f0456d3d46a50945c98de1774da86f8e992ab5c77865ea8b8195341fc19"},
- {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:2c322db9c8c89009a990ef07c3bcc9f011a3269bc06782f916cd3d9eed7c9312"},
- {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:511729f456829ef86ac41ca78c63a5cb55240ed23b4b737faca0eb1abb1c41bc"},
- {file = "charset_normalizer-3.4.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:88ab34806dea0671532d3f82d82b85e8fc23d7b2dd12fa837978dad9bb392a34"},
- {file = "charset_normalizer-3.4.3-cp39-cp39-win32.whl", hash = "sha256:16a8770207946ac75703458e2c743631c79c59c5890c80011d536248f8eaa432"},
- {file = "charset_normalizer-3.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:d22dbedd33326a4a5190dd4fe9e9e693ef12160c77382d9e87919bce54f3d4ca"},
- {file = "charset_normalizer-3.4.3-py3-none-any.whl", hash = "sha256:ce571ab16d890d23b5c278547ba694193a45011ff86a9162a71307ed9f86759a"},
- {file = "charset_normalizer-3.4.3.tar.gz", hash = "sha256:6fce4b8500244f6fcb71465d4a4930d132ba9ab8e71a7859e6a5d59851068d14"},
-]
-
-[[package]]
-name = "click"
-version = "8.2.1"
-description = "Composable command line interface toolkit"
-optional = false
-python-versions = ">=3.10"
-groups = ["main", "dev"]
-files = [
- {file = "click-8.2.1-py3-none-any.whl", hash = "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b"},
- {file = "click-8.2.1.tar.gz", hash = "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202"},
-]
-
-[package.dependencies]
-colorama = {version = "*", markers = "platform_system == \"Windows\""}
-
-[[package]]
-name = "colorama"
-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 = "platform_system == \"Windows\"", dev = "platform_system == \"Windows\" or sys_platform == \"win32\""}
-
-[[package]]
-name = "comm"
-version = "0.2.3"
-description = "Jupyter Python Comm implementation, for usage in ipykernel, xeus-python etc."
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417"},
- {file = "comm-0.2.3.tar.gz", hash = "sha256:2dc8048c10962d55d7ad693be1e7045d891b7ce8d999c97963a5e3e99c055971"},
-]
-
-[package.extras]
-test = ["pytest"]
-
-[[package]]
-name = "datasets"
-version = "2.21.0"
-description = "HuggingFace community-driven open-source library of datasets"
-optional = false
-python-versions = ">=3.8.0"
-groups = ["dev"]
-files = [
- {file = "datasets-2.21.0-py3-none-any.whl", hash = "sha256:25e4e097110ce28824b746a107727ada94024cba11db8bc588d468414692b65a"},
- {file = "datasets-2.21.0.tar.gz", hash = "sha256:998f85a8460f1bd982e5bd058f8a0808eef424249e3df1e8cdd594ccd0dc8ba2"},
-]
-
-[package.dependencies]
-aiohttp = "*"
-dill = ">=0.3.0,<0.3.9"
-filelock = "*"
-fsspec = {version = ">=2023.1.0,<=2024.6.1", extras = ["http"]}
-huggingface-hub = ">=0.21.2"
-multiprocess = "*"
-numpy = ">=1.17"
-packaging = "*"
-pandas = "*"
-pyarrow = ">=15.0.0"
-pyyaml = ">=5.1"
-requests = ">=2.32.2"
-tqdm = ">=4.66.3"
-xxhash = "*"
-
-[package.extras]
-apache-beam = ["apache-beam (>=2.26.0)"]
-audio = ["librosa", "soundfile (>=0.12.1)", "soxr (>=0.4.0) ; python_version >= \"3.9\""]
-benchmarks = ["tensorflow (==2.12.0)", "torch (==2.0.1)", "transformers (==4.30.1)"]
-dev = ["Pillow (>=9.4.0)", "absl-py", "decorator", "elasticsearch (<8.0.0)", "faiss-cpu (>=1.8.0.post1)", "jax (>=0.3.14) ; sys_platform != \"win32\"", "jaxlib (>=0.3.14) ; sys_platform != \"win32\"", "joblib (<1.3.0)", "joblibspark", "librosa", "lz4", "moto[server]", "polars[timezone] (>=0.20.0)", "protobuf (<4.0.0)", "py7zr", "pyspark (>=3.4)", "pytest", "pytest-datadir", "pytest-xdist", "rarfile (>=4.0)", "ruff (>=0.3.0)", "s3fs", "s3fs (>=2021.11.1)", "soundfile (>=0.12.1)", "soxr (>=0.4.0) ; python_version >= \"3.9\"", "sqlalchemy", "tensorflow (>=2.16.0) ; python_version >= \"3.10\"", "tensorflow (>=2.6.0)", "tensorflow (>=2.6.0) ; python_version < \"3.10\"", "tiktoken", "torch", "torch (>=2.0.0)", "transformers", "transformers (>=4.42.0)", "typing-extensions (>=4.6.1)", "zstandard"]
-docs = ["s3fs", "tensorflow (>=2.6.0)", "torch", "transformers"]
-jax = ["jax (>=0.3.14)", "jaxlib (>=0.3.14)"]
-metrics-tests = ["Werkzeug (>=1.0.1)", "accelerate", "bert-score (>=0.3.6)", "jiwer", "langdetect", "mauve-text", "nltk (<3.8.2)", "requests-file (>=1.5.1)", "rouge-score", "sacrebleu", "sacremoses", "scikit-learn", "scipy", "sentencepiece", "seqeval", "six (>=1.15.0,<1.16.0)", "spacy (>=3.0.0)", "texttable (>=1.6.3)", "tldextract", "tldextract (>=3.1.0)", "toml (>=0.10.1)", "typer (<0.5.0)"]
-quality = ["ruff (>=0.3.0)"]
-s3 = ["s3fs"]
-tensorflow = ["tensorflow (>=2.6.0)"]
-tensorflow-gpu = ["tensorflow (>=2.6.0)"]
-tests = ["Pillow (>=9.4.0)", "absl-py", "decorator", "elasticsearch (<8.0.0)", "faiss-cpu (>=1.8.0.post1)", "jax (>=0.3.14) ; sys_platform != \"win32\"", "jaxlib (>=0.3.14) ; sys_platform != \"win32\"", "joblib (<1.3.0)", "joblibspark", "librosa", "lz4", "moto[server]", "polars[timezone] (>=0.20.0)", "protobuf (<4.0.0)", "py7zr", "pyspark (>=3.4)", "pytest", "pytest-datadir", "pytest-xdist", "rarfile (>=4.0)", "s3fs (>=2021.11.1)", "soundfile (>=0.12.1)", "soxr (>=0.4.0) ; python_version >= \"3.9\"", "sqlalchemy", "tensorflow (>=2.16.0) ; python_version >= \"3.10\"", "tensorflow (>=2.6.0) ; python_version < \"3.10\"", "tiktoken", "torch (>=2.0.0)", "transformers (>=4.42.0)", "typing-extensions (>=4.6.1)", "zstandard"]
-tests-numpy2 = ["Pillow (>=9.4.0)", "absl-py", "decorator", "elasticsearch (<8.0.0)", "jax (>=0.3.14) ; sys_platform != \"win32\"", "jaxlib (>=0.3.14) ; sys_platform != \"win32\"", "joblib (<1.3.0)", "joblibspark", "librosa", "lz4", "moto[server]", "polars[timezone] (>=0.20.0)", "protobuf (<4.0.0)", "py7zr", "pyspark (>=3.4)", "pytest", "pytest-datadir", "pytest-xdist", "rarfile (>=4.0)", "s3fs (>=2021.11.1)", "soundfile (>=0.12.1)", "soxr (>=0.4.0) ; python_version >= \"3.9\"", "sqlalchemy", "tiktoken", "torch (>=2.0.0)", "typing-extensions (>=4.6.1)", "zstandard"]
-torch = ["torch"]
-vision = ["Pillow (>=9.4.0)"]
-
-[[package]]
-name = "debugpy"
-version = "1.8.16"
-description = "An implementation of the Debug Adapter Protocol for Python"
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "debugpy-1.8.16-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:2a3958fb9c2f40ed8ea48a0d34895b461de57a1f9862e7478716c35d76f56c65"},
- {file = "debugpy-1.8.16-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5ca7314042e8a614cc2574cd71f6ccd7e13a9708ce3c6d8436959eae56f2378"},
- {file = "debugpy-1.8.16-cp310-cp310-win32.whl", hash = "sha256:8624a6111dc312ed8c363347a0b59c5acc6210d897e41a7c069de3c53235c9a6"},
- {file = "debugpy-1.8.16-cp310-cp310-win_amd64.whl", hash = "sha256:fee6db83ea5c978baf042440cfe29695e1a5d48a30147abf4c3be87513609817"},
- {file = "debugpy-1.8.16-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:67371b28b79a6a12bcc027d94a06158f2fde223e35b5c4e0783b6f9d3b39274a"},
- {file = "debugpy-1.8.16-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2abae6dd02523bec2dee16bd6b0781cccb53fd4995e5c71cc659b5f45581898"},
- {file = "debugpy-1.8.16-cp311-cp311-win32.whl", hash = "sha256:f8340a3ac2ed4f5da59e064aa92e39edd52729a88fbde7bbaa54e08249a04493"},
- {file = "debugpy-1.8.16-cp311-cp311-win_amd64.whl", hash = "sha256:70f5fcd6d4d0c150a878d2aa37391c52de788c3dc680b97bdb5e529cb80df87a"},
- {file = "debugpy-1.8.16-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:b202e2843e32e80b3b584bcebfe0e65e0392920dc70df11b2bfe1afcb7a085e4"},
- {file = "debugpy-1.8.16-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:64473c4a306ba11a99fe0bb14622ba4fbd943eb004847d9b69b107bde45aa9ea"},
- {file = "debugpy-1.8.16-cp312-cp312-win32.whl", hash = "sha256:833a61ed446426e38b0dd8be3e9d45ae285d424f5bf6cd5b2b559c8f12305508"},
- {file = "debugpy-1.8.16-cp312-cp312-win_amd64.whl", hash = "sha256:75f204684581e9ef3dc2f67687c3c8c183fde2d6675ab131d94084baf8084121"},
- {file = "debugpy-1.8.16-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:85df3adb1de5258dca910ae0bb185e48c98801ec15018a263a92bb06be1c8787"},
- {file = "debugpy-1.8.16-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bee89e948bc236a5c43c4214ac62d28b29388453f5fd328d739035e205365f0b"},
- {file = "debugpy-1.8.16-cp313-cp313-win32.whl", hash = "sha256:cf358066650439847ec5ff3dae1da98b5461ea5da0173d93d5e10f477c94609a"},
- {file = "debugpy-1.8.16-cp313-cp313-win_amd64.whl", hash = "sha256:b5aea1083f6f50023e8509399d7dc6535a351cc9f2e8827d1e093175e4d9fa4c"},
- {file = "debugpy-1.8.16-cp38-cp38-macosx_14_0_x86_64.whl", hash = "sha256:2801329c38f77c47976d341d18040a9ac09d0c71bf2c8b484ad27c74f83dc36f"},
- {file = "debugpy-1.8.16-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:687c7ab47948697c03b8f81424aa6dc3f923e6ebab1294732df1ca9773cc67bc"},
- {file = "debugpy-1.8.16-cp38-cp38-win32.whl", hash = "sha256:a2ba6fc5d7c4bc84bcae6c5f8edf5988146e55ae654b1bb36fecee9e5e77e9e2"},
- {file = "debugpy-1.8.16-cp38-cp38-win_amd64.whl", hash = "sha256:d58c48d8dbbbf48a3a3a638714a2d16de537b0dace1e3432b8e92c57d43707f8"},
- {file = "debugpy-1.8.16-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:135ccd2b1161bade72a7a099c9208811c137a150839e970aeaf121c2467debe8"},
- {file = "debugpy-1.8.16-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:211238306331a9089e253fd997213bc4a4c65f949271057d6695953254095376"},
- {file = "debugpy-1.8.16-cp39-cp39-win32.whl", hash = "sha256:88eb9ffdfb59bf63835d146c183d6dba1f722b3ae2a5f4b9fc03e925b3358922"},
- {file = "debugpy-1.8.16-cp39-cp39-win_amd64.whl", hash = "sha256:c2c47c2e52b40449552843b913786499efcc3dbc21d6c49287d939cd0dbc49fd"},
- {file = "debugpy-1.8.16-py2.py3-none-any.whl", hash = "sha256:19c9521962475b87da6f673514f7fd610328757ec993bf7ec0d8c96f9a325f9e"},
- {file = "debugpy-1.8.16.tar.gz", hash = "sha256:31e69a1feb1cf6b51efbed3f6c9b0ef03bc46ff050679c4be7ea6d2e23540870"},
-]
-
-[[package]]
-name = "decorator"
-version = "5.2.1"
-description = "Decorators for Humans"
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a"},
- {file = "decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360"},
-]
-
-[[package]]
-name = "defusedxml"
-version = "0.7.1"
-description = "XML bomb protection for Python stdlib modules"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-groups = ["dev"]
-files = [
- {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"},
- {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"},
-]
-
-[[package]]
-name = "dill"
-version = "0.3.8"
-description = "serialize all of Python"
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"},
- {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"},
-]
-
-[package.extras]
-graph = ["objgraph (>=1.7.2)"]
-profile = ["gprof2dot (>=2022.7.29)"]
-
-[[package]]
-name = "distlib"
-version = "0.4.0"
-description = "Distribution utilities"
-optional = false
-python-versions = "*"
-groups = ["main"]
-files = [
- {file = "distlib-0.4.0-py2.py3-none-any.whl", hash = "sha256:9659f7d87e46584a30b5780e43ac7a2143098441670ff0a49d5f9034c54a6c16"},
- {file = "distlib-0.4.0.tar.gz", hash = "sha256:feec40075be03a04501a973d81f633735b4b69f98b05450592310c0f401a4e0d"},
-]
-
-[[package]]
-name = "einops"
-version = "0.8.1"
-description = "A new flavour of deep learning operations"
-optional = false
-python-versions = ">=3.8"
-groups = ["main"]
-files = [
- {file = "einops-0.8.1-py3-none-any.whl", hash = "sha256:919387eb55330f5757c6bea9165c5ff5cfe63a642682ea788a6d472576d81737"},
- {file = "einops-0.8.1.tar.gz", hash = "sha256:de5d960a7a761225532e0f1959e5315ebeafc0cd43394732f103ca44b9837e84"},
-]
-
-[[package]]
-name = "exceptiongroup"
-version = "1.3.0"
-description = "Backport of PEP 654 (exception groups)"
-optional = false
-python-versions = ">=3.7"
-groups = ["dev"]
-markers = "python_version == \"3.10\""
-files = [
- {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"},
- {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"},
-]
-
-[package.dependencies]
-typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""}
-
-[package.extras]
-test = ["pytest (>=6)"]
-
-[[package]]
-name = "executing"
-version = "2.2.1"
-description = "Get the currently executing AST node of a frame, and other information"
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017"},
- {file = "executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4"},
-]
-
-[package.extras]
-tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich ; python_version >= \"3.11\""]
-
-[[package]]
-name = "fastjsonschema"
-version = "2.21.2"
-description = "Fastest Python implementation of JSON schema"
-optional = false
-python-versions = "*"
-groups = ["dev"]
-files = [
- {file = "fastjsonschema-2.21.2-py3-none-any.whl", hash = "sha256:1c797122d0a86c5cace2e54bf4e819c36223b552017172f32c5c024a6b77e463"},
- {file = "fastjsonschema-2.21.2.tar.gz", hash = "sha256:b1eb43748041c880796cd077f1a07c3d94e93ae84bba5ed36800a33554ae05de"},
-]
-
-[package.extras]
-devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benchmark", "pytest-cache", "validictory"]
-
-[[package]]
-name = "filelock"
-version = "3.19.1"
-description = "A platform independent file lock."
-optional = false
-python-versions = ">=3.9"
-groups = ["main", "dev"]
-files = [
- {file = "filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d"},
- {file = "filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58"},
-]
-
-[[package]]
-name = "filetype"
-version = "1.2.0"
-description = "Infer file type and MIME type of any file/buffer. No external dependencies."
-optional = false
-python-versions = "*"
-groups = ["main"]
-files = [
- {file = "filetype-1.2.0-py2.py3-none-any.whl", hash = "sha256:7ce71b6880181241cf7ac8697a2f1eb6a8bd9b429f7ad6d27b8db9ba5f1c2d25"},
- {file = "filetype-1.2.0.tar.gz", hash = "sha256:66b56cd6474bf41d8c54660347d37afcc3f7d1970648de365c102ef77548aadb"},
-]
-
-[[package]]
-name = "fqdn"
-version = "1.5.1"
-description = "Validates fully-qualified domain names against RFC 1123, so that they are acceptable to modern bowsers"
-optional = false
-python-versions = ">=2.7, !=3.0, !=3.1, !=3.2, !=3.3, !=3.4, <4"
-groups = ["dev"]
-files = [
- {file = "fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014"},
- {file = "fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f"},
-]
-
-[[package]]
-name = "frozenlist"
-version = "1.7.0"
-description = "A list-like structure which implements collections.abc.MutableSequence"
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "frozenlist-1.7.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cc4df77d638aa2ed703b878dd093725b72a824c3c546c076e8fdf276f78ee84a"},
- {file = "frozenlist-1.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:716a9973a2cc963160394f701964fe25012600f3d311f60c790400b00e568b61"},
- {file = "frozenlist-1.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0fd1bad056a3600047fb9462cff4c5322cebc59ebf5d0a3725e0ee78955001d"},
- {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3789ebc19cb811163e70fe2bd354cea097254ce6e707ae42e56f45e31e96cb8e"},
- {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af369aa35ee34f132fcfad5be45fbfcde0e3a5f6a1ec0712857f286b7d20cca9"},
- {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac64b6478722eeb7a3313d494f8342ef3478dff539d17002f849101b212ef97c"},
- {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f89f65d85774f1797239693cef07ad4c97fdd0639544bad9ac4b869782eb1981"},
- {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1073557c941395fdfcfac13eb2456cb8aad89f9de27bae29fabca8e563b12615"},
- {file = "frozenlist-1.7.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ed8d2fa095aae4bdc7fdd80351009a48d286635edffee66bf865e37a9125c50"},
- {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:24c34bea555fe42d9f928ba0a740c553088500377448febecaa82cc3e88aa1fa"},
- {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:69cac419ac6a6baad202c85aaf467b65ac860ac2e7f2ac1686dc40dbb52f6577"},
- {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:960d67d0611f4c87da7e2ae2eacf7ea81a5be967861e0c63cf205215afbfac59"},
- {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:41be2964bd4b15bf575e5daee5a5ce7ed3115320fb3c2b71fca05582ffa4dc9e"},
- {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:46d84d49e00c9429238a7ce02dc0be8f6d7cd0cd405abd1bebdc991bf27c15bd"},
- {file = "frozenlist-1.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:15900082e886edb37480335d9d518cec978afc69ccbc30bd18610b7c1b22a718"},
- {file = "frozenlist-1.7.0-cp310-cp310-win32.whl", hash = "sha256:400ddd24ab4e55014bba442d917203c73b2846391dd42ca5e38ff52bb18c3c5e"},
- {file = "frozenlist-1.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:6eb93efb8101ef39d32d50bce242c84bcbddb4f7e9febfa7b524532a239b4464"},
- {file = "frozenlist-1.7.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:aa51e147a66b2d74de1e6e2cf5921890de6b0f4820b257465101d7f37b49fb5a"},
- {file = "frozenlist-1.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9b35db7ce1cd71d36ba24f80f0c9e7cff73a28d7a74e91fe83e23d27c7828750"},
- {file = "frozenlist-1.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34a69a85e34ff37791e94542065c8416c1afbf820b68f720452f636d5fb990cd"},
- {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a646531fa8d82c87fe4bb2e596f23173caec9185bfbca5d583b4ccfb95183e2"},
- {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:79b2ffbba483f4ed36a0f236ccb85fbb16e670c9238313709638167670ba235f"},
- {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a26f205c9ca5829cbf82bb2a84b5c36f7184c4316617d7ef1b271a56720d6b30"},
- {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bcacfad3185a623fa11ea0e0634aac7b691aa925d50a440f39b458e41c561d98"},
- {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72c1b0fe8fe451b34f12dce46445ddf14bd2a5bcad7e324987194dc8e3a74c86"},
- {file = "frozenlist-1.7.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61d1a5baeaac6c0798ff6edfaeaa00e0e412d49946c53fae8d4b8e8b3566c4ae"},
- {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7edf5c043c062462f09b6820de9854bf28cc6cc5b6714b383149745e287181a8"},
- {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:d50ac7627b3a1bd2dcef6f9da89a772694ec04d9a61b66cf87f7d9446b4a0c31"},
- {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:ce48b2fece5aeb45265bb7a58259f45027db0abff478e3077e12b05b17fb9da7"},
- {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:fe2365ae915a1fafd982c146754e1de6ab3478def8a59c86e1f7242d794f97d5"},
- {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:45a6f2fdbd10e074e8814eb98b05292f27bad7d1883afbe009d96abdcf3bc898"},
- {file = "frozenlist-1.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:21884e23cffabb157a9dd7e353779077bf5b8f9a58e9b262c6caad2ef5f80a56"},
- {file = "frozenlist-1.7.0-cp311-cp311-win32.whl", hash = "sha256:284d233a8953d7b24f9159b8a3496fc1ddc00f4db99c324bd5fb5f22d8698ea7"},
- {file = "frozenlist-1.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:387cbfdcde2f2353f19c2f66bbb52406d06ed77519ac7ee21be0232147c2592d"},
- {file = "frozenlist-1.7.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:3dbf9952c4bb0e90e98aec1bd992b3318685005702656bc6f67c1a32b76787f2"},
- {file = "frozenlist-1.7.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1f5906d3359300b8a9bb194239491122e6cf1444c2efb88865426f170c262cdb"},
- {file = "frozenlist-1.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3dabd5a8f84573c8d10d8859a50ea2dec01eea372031929871368c09fa103478"},
- {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa57daa5917f1738064f302bf2626281a1cb01920c32f711fbc7bc36111058a8"},
- {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c193dda2b6d49f4c4398962810fa7d7c78f032bf45572b3e04dd5249dff27e08"},
- {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfe2b675cf0aaa6d61bf8fbffd3c274b3c9b7b1623beb3809df8a81399a4a9c4"},
- {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8fc5d5cda37f62b262405cf9652cf0856839c4be8ee41be0afe8858f17f4c94b"},
- {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0d5ce521d1dd7d620198829b87ea002956e4319002ef0bc8d3e6d045cb4646e"},
- {file = "frozenlist-1.7.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:488d0a7d6a0008ca0db273c542098a0fa9e7dfaa7e57f70acef43f32b3f69dca"},
- {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:15a7eaba63983d22c54d255b854e8108e7e5f3e89f647fc854bd77a237e767df"},
- {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1eaa7e9c6d15df825bf255649e05bd8a74b04a4d2baa1ae46d9c2d00b2ca2cb5"},
- {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4389e06714cfa9d47ab87f784a7c5be91d3934cd6e9a7b85beef808297cc025"},
- {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:73bd45e1488c40b63fe5a7df892baf9e2a4d4bb6409a2b3b78ac1c6236178e01"},
- {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:99886d98e1643269760e5fe0df31e5ae7050788dd288947f7f007209b8c33f08"},
- {file = "frozenlist-1.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:290a172aae5a4c278c6da8a96222e6337744cd9c77313efe33d5670b9f65fc43"},
- {file = "frozenlist-1.7.0-cp312-cp312-win32.whl", hash = "sha256:426c7bc70e07cfebc178bc4c2bf2d861d720c4fff172181eeb4a4c41d4ca2ad3"},
- {file = "frozenlist-1.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:563b72efe5da92e02eb68c59cb37205457c977aa7a449ed1b37e6939e5c47c6a"},
- {file = "frozenlist-1.7.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ee80eeda5e2a4e660651370ebffd1286542b67e268aa1ac8d6dbe973120ef7ee"},
- {file = "frozenlist-1.7.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d1a81c85417b914139e3a9b995d4a1c84559afc839a93cf2cb7f15e6e5f6ed2d"},
- {file = "frozenlist-1.7.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cbb65198a9132ebc334f237d7b0df163e4de83fb4f2bdfe46c1e654bdb0c5d43"},
- {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dab46c723eeb2c255a64f9dc05b8dd601fde66d6b19cdb82b2e09cc6ff8d8b5d"},
- {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6aeac207a759d0dedd2e40745575ae32ab30926ff4fa49b1635def65806fddee"},
- {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bd8c4e58ad14b4fa7802b8be49d47993182fdd4023393899632c88fd8cd994eb"},
- {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04fb24d104f425da3540ed83cbfc31388a586a7696142004c577fa61c6298c3f"},
- {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a5c505156368e4ea6b53b5ac23c92d7edc864537ff911d2fb24c140bb175e60"},
- {file = "frozenlist-1.7.0-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8bd7eb96a675f18aa5c553eb7ddc24a43c8c18f22e1f9925528128c052cdbe00"},
- {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:05579bf020096fe05a764f1f84cd104a12f78eaab68842d036772dc6d4870b4b"},
- {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:376b6222d114e97eeec13d46c486facd41d4f43bab626b7c3f6a8b4e81a5192c"},
- {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:0aa7e176ebe115379b5b1c95b4096fb1c17cce0847402e227e712c27bdb5a949"},
- {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3fbba20e662b9c2130dc771e332a99eff5da078b2b2648153a40669a6d0e36ca"},
- {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:f3f4410a0a601d349dd406b5713fec59b4cee7e71678d5b17edda7f4655a940b"},
- {file = "frozenlist-1.7.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e2cdfaaec6a2f9327bf43c933c0319a7c429058e8537c508964a133dffee412e"},
- {file = "frozenlist-1.7.0-cp313-cp313-win32.whl", hash = "sha256:5fc4df05a6591c7768459caba1b342d9ec23fa16195e744939ba5914596ae3e1"},
- {file = "frozenlist-1.7.0-cp313-cp313-win_amd64.whl", hash = "sha256:52109052b9791a3e6b5d1b65f4b909703984b770694d3eb64fad124c835d7cba"},
- {file = "frozenlist-1.7.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a6f86e4193bb0e235ef6ce3dde5cbabed887e0b11f516ce8a0f4d3b33078ec2d"},
- {file = "frozenlist-1.7.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:82d664628865abeb32d90ae497fb93df398a69bb3434463d172b80fc25b0dd7d"},
- {file = "frozenlist-1.7.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:912a7e8375a1c9a68325a902f3953191b7b292aa3c3fb0d71a216221deca460b"},
- {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9537c2777167488d539bc5de2ad262efc44388230e5118868e172dd4a552b146"},
- {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f34560fb1b4c3e30ba35fa9a13894ba39e5acfc5f60f57d8accde65f46cc5e74"},
- {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:acd03d224b0175f5a850edc104ac19040d35419eddad04e7cf2d5986d98427f1"},
- {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2038310bc582f3d6a09b3816ab01737d60bf7b1ec70f5356b09e84fb7408ab1"},
- {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b8c05e4c8e5f36e5e088caa1bf78a687528f83c043706640a92cb76cd6999384"},
- {file = "frozenlist-1.7.0-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:765bb588c86e47d0b68f23c1bee323d4b703218037765dcf3f25c838c6fecceb"},
- {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:32dc2e08c67d86d0969714dd484fd60ff08ff81d1a1e40a77dd34a387e6ebc0c"},
- {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:c0303e597eb5a5321b4de9c68e9845ac8f290d2ab3f3e2c864437d3c5a30cd65"},
- {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:a47f2abb4e29b3a8d0b530f7c3598badc6b134562b1a5caee867f7c62fee51e3"},
- {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:3d688126c242a6fabbd92e02633414d40f50bb6002fa4cf995a1d18051525657"},
- {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:4e7e9652b3d367c7bd449a727dc79d5043f48b88d0cbfd4f9f1060cf2b414104"},
- {file = "frozenlist-1.7.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:1a85e345b4c43db8b842cab1feb41be5cc0b10a1830e6295b69d7310f99becaf"},
- {file = "frozenlist-1.7.0-cp313-cp313t-win32.whl", hash = "sha256:3a14027124ddb70dfcee5148979998066897e79f89f64b13328595c4bdf77c81"},
- {file = "frozenlist-1.7.0-cp313-cp313t-win_amd64.whl", hash = "sha256:3bf8010d71d4507775f658e9823210b7427be36625b387221642725b515dcf3e"},
- {file = "frozenlist-1.7.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cea3dbd15aea1341ea2de490574a4a37ca080b2ae24e4b4f4b51b9057b4c3630"},
- {file = "frozenlist-1.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7d536ee086b23fecc36c2073c371572374ff50ef4db515e4e503925361c24f71"},
- {file = "frozenlist-1.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dfcebf56f703cb2e346315431699f00db126d158455e513bd14089d992101e44"},
- {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:974c5336e61d6e7eb1ea5b929cb645e882aadab0095c5a6974a111e6479f8878"},
- {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c70db4a0ab5ab20878432c40563573229a7ed9241506181bba12f6b7d0dc41cb"},
- {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1137b78384eebaf70560a36b7b229f752fb64d463d38d1304939984d5cb887b6"},
- {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e793a9f01b3e8b5c0bc646fb59140ce0efcc580d22a3468d70766091beb81b35"},
- {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74739ba8e4e38221d2c5c03d90a7e542cb8ad681915f4ca8f68d04f810ee0a87"},
- {file = "frozenlist-1.7.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e63344c4e929b1a01e29bc184bbb5fd82954869033765bfe8d65d09e336a677"},
- {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2ea2a7369eb76de2217a842f22087913cdf75f63cf1307b9024ab82dfb525938"},
- {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:836b42f472a0e006e02499cef9352ce8097f33df43baaba3e0a28a964c26c7d2"},
- {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e22b9a99741294b2571667c07d9f8cceec07cb92aae5ccda39ea1b6052ed4319"},
- {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:9a19e85cc503d958abe5218953df722748d87172f71b73cf3c9257a91b999890"},
- {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:f22dac33bb3ee8fe3e013aa7b91dc12f60d61d05b7fe32191ffa84c3aafe77bd"},
- {file = "frozenlist-1.7.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9ccec739a99e4ccf664ea0775149f2749b8a6418eb5b8384b4dc0a7d15d304cb"},
- {file = "frozenlist-1.7.0-cp39-cp39-win32.whl", hash = "sha256:b3950f11058310008a87757f3eee16a8e1ca97979833239439586857bc25482e"},
- {file = "frozenlist-1.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:43a82fce6769c70f2f5a06248b614a7d268080a9d20f7457ef10ecee5af82b63"},
- {file = "frozenlist-1.7.0-py3-none-any.whl", hash = "sha256:9a5af342e34f7e97caf8c995864c7a396418ae2859cc6fdf1b1073020d516a7e"},
- {file = "frozenlist-1.7.0.tar.gz", hash = "sha256:2e310d81923c2437ea8670467121cc3e9b0f76d3043cc1d2331d56c7fb7a3a8f"},
-]
-
-[[package]]
-name = "fsspec"
-version = "2024.6.1"
-description = "File-system specification"
-optional = false
-python-versions = ">=3.8"
-groups = ["main", "dev"]
-files = [
- {file = "fsspec-2024.6.1-py3-none-any.whl", hash = "sha256:3cb443f8bcd2efb31295a5b9fdb02aee81d8452c80d28f97a6d0959e6cee101e"},
- {file = "fsspec-2024.6.1.tar.gz", hash = "sha256:fad7d7e209dd4c1208e3bbfda706620e0da5142bebbd9c384afb95b07e798e49"},
-]
-
-[package.dependencies]
-aiohttp = {version = "<4.0.0a0 || >4.0.0a0,<4.0.0a1 || >4.0.0a1", optional = true, markers = "extra == \"http\""}
-
-[package.extras]
-abfs = ["adlfs"]
-adl = ["adlfs"]
-arrow = ["pyarrow (>=1)"]
-dask = ["dask", "distributed"]
-dev = ["pre-commit", "ruff"]
-doc = ["numpydoc", "sphinx", "sphinx-design", "sphinx-rtd-theme", "yarl"]
-dropbox = ["dropbox", "dropboxdrivefs", "requests"]
-full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "dask", "distributed", "dropbox", "dropboxdrivefs", "fusepy", "gcsfs", "libarchive-c", "ocifs", "panel", "paramiko", "pyarrow (>=1)", "pygit2", "requests", "s3fs", "smbprotocol", "tqdm"]
-fuse = ["fusepy"]
-gcs = ["gcsfs"]
-git = ["pygit2"]
-github = ["requests"]
-gs = ["gcsfs"]
-gui = ["panel"]
-hdfs = ["pyarrow (>=1)"]
-http = ["aiohttp (!=4.0.0a0,!=4.0.0a1)"]
-libarchive = ["libarchive-c"]
-oci = ["ocifs"]
-s3 = ["s3fs"]
-sftp = ["paramiko"]
-smb = ["smbprotocol"]
-ssh = ["paramiko"]
-test = ["aiohttp (!=4.0.0a0,!=4.0.0a1)", "numpy", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "requests"]
-test-downstream = ["aiobotocore (>=2.5.4,<3.0.0)", "dask-expr", "dask[dataframe,test]", "moto[server] (>4,<5)", "pytest-timeout", "xarray"]
-test-full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "cloudpickle", "dask", "distributed", "dropbox", "dropboxdrivefs", "fastparquet", "fusepy", "gcsfs", "jinja2", "kerchunk", "libarchive-c", "lz4", "notebook", "numpy", "ocifs", "pandas", "panel", "paramiko", "pyarrow", "pyarrow (>=1)", "pyftpdlib", "pygit2", "pytest", "pytest-asyncio (!=0.22.0)", "pytest-benchmark", "pytest-cov", "pytest-mock", "pytest-recording", "pytest-rerunfailures", "python-snappy", "requests", "smbprotocol", "tqdm", "urllib3", "zarr", "zstandard"]
-tqdm = ["tqdm"]
-
-[[package]]
-name = "gitdb"
-version = "4.0.12"
-description = "Git Object Database"
-optional = false
-python-versions = ">=3.7"
-groups = ["dev"]
-files = [
- {file = "gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf"},
- {file = "gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571"},
-]
-
-[package.dependencies]
-smmap = ">=3.0.1,<6"
-
-[[package]]
-name = "gitpython"
-version = "3.1.45"
-description = "GitPython is a Python library used to interact with Git repositories"
-optional = false
-python-versions = ">=3.7"
-groups = ["dev"]
-files = [
- {file = "gitpython-3.1.45-py3-none-any.whl", hash = "sha256:8908cb2e02fb3b93b7eb0f2827125cb699869470432cc885f019b8fd0fccff77"},
- {file = "gitpython-3.1.45.tar.gz", hash = "sha256:85b0ee964ceddf211c41b9f27a49086010a190fd8132a24e21f362a4b36a791c"},
-]
-
-[package.dependencies]
-gitdb = ">=4.0.1,<5"
-
-[package.extras]
-doc = ["sphinx (>=7.1.2,<7.2)", "sphinx-autodoc-typehints", "sphinx_rtd_theme"]
-test = ["coverage[toml]", "ddt (>=1.1.1,!=1.4.3)", "mock ; python_version < \"3.8\"", "mypy", "pre-commit", "pytest (>=7.3.1)", "pytest-cov", "pytest-instafail", "pytest-mock", "pytest-sugar", "typing-extensions ; python_version < \"3.11\""]
-
-[[package]]
-name = "grpcio"
-version = "1.74.0"
-description = "HTTP/2-based RPC framework"
-optional = false
-python-versions = ">=3.9"
-groups = ["xla"]
-files = [
- {file = "grpcio-1.74.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:85bd5cdf4ed7b2d6438871adf6afff9af7096486fcf51818a81b77ef4dd30907"},
- {file = "grpcio-1.74.0-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:68c8ebcca945efff9d86d8d6d7bfb0841cf0071024417e2d7f45c5e46b5b08eb"},
- {file = "grpcio-1.74.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:e154d230dc1bbbd78ad2fdc3039fa50ad7ffcf438e4eb2fa30bce223a70c7486"},
- {file = "grpcio-1.74.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8978003816c7b9eabe217f88c78bc26adc8f9304bf6a594b02e5a49b2ef9c11"},
- {file = "grpcio-1.74.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3d7bd6e3929fd2ea7fbc3f562e4987229ead70c9ae5f01501a46701e08f1ad9"},
- {file = "grpcio-1.74.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:136b53c91ac1d02c8c24201bfdeb56f8b3ac3278668cbb8e0ba49c88069e1bdc"},
- {file = "grpcio-1.74.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fe0f540750a13fd8e5da4b3eaba91a785eea8dca5ccd2bc2ffe978caa403090e"},
- {file = "grpcio-1.74.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4e4181bfc24413d1e3a37a0b7889bea68d973d4b45dd2bc68bb766c140718f82"},
- {file = "grpcio-1.74.0-cp310-cp310-win32.whl", hash = "sha256:1733969040989f7acc3d94c22f55b4a9501a30f6aaacdbccfaba0a3ffb255ab7"},
- {file = "grpcio-1.74.0-cp310-cp310-win_amd64.whl", hash = "sha256:9e912d3c993a29df6c627459af58975b2e5c897d93287939b9d5065f000249b5"},
- {file = "grpcio-1.74.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:69e1a8180868a2576f02356565f16635b99088da7df3d45aaa7e24e73a054e31"},
- {file = "grpcio-1.74.0-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:8efe72fde5500f47aca1ef59495cb59c885afe04ac89dd11d810f2de87d935d4"},
- {file = "grpcio-1.74.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:a8f0302f9ac4e9923f98d8e243939a6fb627cd048f5cd38595c97e38020dffce"},
- {file = "grpcio-1.74.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f609a39f62a6f6f05c7512746798282546358a37ea93c1fcbadf8b2fed162e3"},
- {file = "grpcio-1.74.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c98e0b7434a7fa4e3e63f250456eaef52499fba5ae661c58cc5b5477d11e7182"},
- {file = "grpcio-1.74.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:662456c4513e298db6d7bd9c3b8df6f75f8752f0ba01fb653e252ed4a59b5a5d"},
- {file = "grpcio-1.74.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3d14e3c4d65e19d8430a4e28ceb71ace4728776fd6c3ce34016947474479683f"},
- {file = "grpcio-1.74.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bf949792cee20d2078323a9b02bacbbae002b9e3b9e2433f2741c15bdeba1c4"},
- {file = "grpcio-1.74.0-cp311-cp311-win32.whl", hash = "sha256:55b453812fa7c7ce2f5c88be3018fb4a490519b6ce80788d5913f3f9d7da8c7b"},
- {file = "grpcio-1.74.0-cp311-cp311-win_amd64.whl", hash = "sha256:86ad489db097141a907c559988c29718719aa3e13370d40e20506f11b4de0d11"},
- {file = "grpcio-1.74.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:8533e6e9c5bd630ca98062e3a1326249e6ada07d05acf191a77bc33f8948f3d8"},
- {file = "grpcio-1.74.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:2918948864fec2a11721d91568effffbe0a02b23ecd57f281391d986847982f6"},
- {file = "grpcio-1.74.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:60d2d48b0580e70d2e1954d0d19fa3c2e60dd7cbed826aca104fff518310d1c5"},
- {file = "grpcio-1.74.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3601274bc0523f6dc07666c0e01682c94472402ac2fd1226fd96e079863bfa49"},
- {file = "grpcio-1.74.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:176d60a5168d7948539def20b2a3adcce67d72454d9ae05969a2e73f3a0feee7"},
- {file = "grpcio-1.74.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e759f9e8bc908aaae0412642afe5416c9f983a80499448fcc7fab8692ae044c3"},
- {file = "grpcio-1.74.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:9e7c4389771855a92934b2846bd807fc25a3dfa820fd912fe6bd8136026b2707"},
- {file = "grpcio-1.74.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cce634b10aeab37010449124814b05a62fb5f18928ca878f1bf4750d1f0c815b"},
- {file = "grpcio-1.74.0-cp312-cp312-win32.whl", hash = "sha256:885912559974df35d92219e2dc98f51a16a48395f37b92865ad45186f294096c"},
- {file = "grpcio-1.74.0-cp312-cp312-win_amd64.whl", hash = "sha256:42f8fee287427b94be63d916c90399ed310ed10aadbf9e2e5538b3e497d269bc"},
- {file = "grpcio-1.74.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:2bc2d7d8d184e2362b53905cb1708c84cb16354771c04b490485fa07ce3a1d89"},
- {file = "grpcio-1.74.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:c14e803037e572c177ba54a3e090d6eb12efd795d49327c5ee2b3bddb836bf01"},
- {file = "grpcio-1.74.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:f6ec94f0e50eb8fa1744a731088b966427575e40c2944a980049798b127a687e"},
- {file = "grpcio-1.74.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:566b9395b90cc3d0d0c6404bc8572c7c18786ede549cdb540ae27b58afe0fb91"},
- {file = "grpcio-1.74.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1ea6176d7dfd5b941ea01c2ec34de9531ba494d541fe2057c904e601879f249"},
- {file = "grpcio-1.74.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:64229c1e9cea079420527fa8ac45d80fc1e8d3f94deaa35643c381fa8d98f362"},
- {file = "grpcio-1.74.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:0f87bddd6e27fc776aacf7ebfec367b6d49cad0455123951e4488ea99d9b9b8f"},
- {file = "grpcio-1.74.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:3b03d8f2a07f0fea8c8f74deb59f8352b770e3900d143b3d1475effcb08eec20"},
- {file = "grpcio-1.74.0-cp313-cp313-win32.whl", hash = "sha256:b6a73b2ba83e663b2480a90b82fdae6a7aa6427f62bf43b29912c0cfd1aa2bfa"},
- {file = "grpcio-1.74.0-cp313-cp313-win_amd64.whl", hash = "sha256:fd3c71aeee838299c5887230b8a1822795325ddfea635edd82954c1eaa831e24"},
- {file = "grpcio-1.74.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:4bc5fca10aaf74779081e16c2bcc3d5ec643ffd528d9e7b1c9039000ead73bae"},
- {file = "grpcio-1.74.0-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:6bab67d15ad617aff094c382c882e0177637da73cbc5532d52c07b4ee887a87b"},
- {file = "grpcio-1.74.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:655726919b75ab3c34cdad39da5c530ac6fa32696fb23119e36b64adcfca174a"},
- {file = "grpcio-1.74.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a2b06afe2e50ebfd46247ac3ba60cac523f54ec7792ae9ba6073c12daf26f0a"},
- {file = "grpcio-1.74.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f251c355167b2360537cf17bea2cf0197995e551ab9da6a0a59b3da5e8704f9"},
- {file = "grpcio-1.74.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8f7b5882fb50632ab1e48cb3122d6df55b9afabc265582808036b6e51b9fd6b7"},
- {file = "grpcio-1.74.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:834988b6c34515545b3edd13e902c1acdd9f2465d386ea5143fb558f153a7176"},
- {file = "grpcio-1.74.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:22b834cef33429ca6cc28303c9c327ba9a3fafecbf62fae17e9a7b7163cc43ac"},
- {file = "grpcio-1.74.0-cp39-cp39-win32.whl", hash = "sha256:7d95d71ff35291bab3f1c52f52f474c632db26ea12700c2ff0ea0532cb0b5854"},
- {file = "grpcio-1.74.0-cp39-cp39-win_amd64.whl", hash = "sha256:ecde9ab49f58433abe02f9ed076c7b5be839cf0153883a6d23995937a82392fa"},
- {file = "grpcio-1.74.0.tar.gz", hash = "sha256:80d1f4fbb35b0742d3e3d3bb654b7381cd5f015f8497279a1e9c21ba623e01b1"},
-]
-
-[package.extras]
-protobuf = ["grpcio-tools (>=1.74.0)"]
-
-[[package]]
-name = "h11"
-version = "0.16.0"
-description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1"
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86"},
- {file = "h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1"},
-]
-
-[[package]]
-name = "hf-xet"
-version = "1.1.9"
-description = "Fast transfer of large files with the Hugging Face Hub."
-optional = false
-python-versions = ">=3.8"
-groups = ["main", "dev"]
-markers = "platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"arm64\" or platform_machine == \"aarch64\""
-files = [
- {file = "hf_xet-1.1.9-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:a3b6215f88638dd7a6ff82cb4e738dcbf3d863bf667997c093a3c990337d1160"},
- {file = "hf_xet-1.1.9-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:9b486de7a64a66f9a172f4b3e0dfe79c9f0a93257c501296a2521a13495a698a"},
- {file = "hf_xet-1.1.9-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a4c5a840c2c4e6ec875ed13703a60e3523bc7f48031dfd750923b2a4d1a5fc3c"},
- {file = "hf_xet-1.1.9-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:96a6139c9e44dad1c52c52520db0fffe948f6bce487cfb9d69c125f254bb3790"},
- {file = "hf_xet-1.1.9-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ad1022e9a998e784c97b2173965d07fe33ee26e4594770b7785a8cc8f922cd95"},
- {file = "hf_xet-1.1.9-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:86754c2d6d5afb11b0a435e6e18911a4199262fe77553f8c50d75e21242193ea"},
- {file = "hf_xet-1.1.9-cp37-abi3-win_amd64.whl", hash = "sha256:5aad3933de6b725d61d51034e04174ed1dce7a57c63d530df0014dea15a40127"},
- {file = "hf_xet-1.1.9.tar.gz", hash = "sha256:c99073ce404462e909f1d5839b2d14a3827b8fe75ed8aed551ba6609c026c803"},
-]
-
-[package.extras]
-tests = ["pytest"]
-
-[[package]]
-name = "httpcore"
-version = "1.0.9"
-description = "A minimal low-level HTTP client."
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55"},
- {file = "httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8"},
-]
-
-[package.dependencies]
-certifi = "*"
-h11 = ">=0.16"
-
-[package.extras]
-asyncio = ["anyio (>=4.0,<5.0)"]
-http2 = ["h2 (>=3,<5)"]
-socks = ["socksio (==1.*)"]
-trio = ["trio (>=0.22.0,<1.0)"]
-
-[[package]]
-name = "httpx"
-version = "0.28.1"
-description = "The next generation HTTP client."
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"},
- {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"},
-]
-
-[package.dependencies]
-anyio = "*"
-certifi = "*"
-httpcore = "==1.*"
-idna = "*"
-
-[package.extras]
-brotli = ["brotli ; platform_python_implementation == \"CPython\"", "brotlicffi ; platform_python_implementation != \"CPython\""]
-cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"]
-http2 = ["h2 (>=3,<5)"]
-socks = ["socksio (==1.*)"]
-zstd = ["zstandard (>=0.18.0)"]
-
-[[package]]
-name = "huggingface-hub"
-version = "0.34.4"
-description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub"
-optional = false
-python-versions = ">=3.8.0"
-groups = ["main", "dev"]
-files = [
- {file = "huggingface_hub-0.34.4-py3-none-any.whl", hash = "sha256:9b365d781739c93ff90c359844221beef048403f1bc1f1c123c191257c3c890a"},
- {file = "huggingface_hub-0.34.4.tar.gz", hash = "sha256:a4228daa6fb001be3f4f4bdaf9a0db00e1739235702848df00885c9b5742c85c"},
-]
-
-[package.dependencies]
-filelock = "*"
-fsspec = ">=2023.5.0"
-hf-xet = {version = ">=1.1.3,<2.0.0", markers = "platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"arm64\" or platform_machine == \"aarch64\""}
-packaging = ">=20.9"
-pyyaml = ">=5.1"
-requests = "*"
-tqdm = ">=4.42.1"
-typing-extensions = ">=3.7.4.3"
-
-[package.extras]
-all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "authlib (>=1.3.2)", "fastapi", "gradio (>=4.0.0)", "httpx", "itsdangerous", "jedi", "libcst (>=1.4.0)", "mypy (==1.15.0) ; python_version >= \"3.9\"", "mypy (>=1.14.1,<1.15.0) ; python_version == \"3.8\"", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.9.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"]
-cli = ["InquirerPy (==0.3.4)"]
-dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "authlib (>=1.3.2)", "fastapi", "gradio (>=4.0.0)", "httpx", "itsdangerous", "jedi", "libcst (>=1.4.0)", "mypy (==1.15.0) ; python_version >= \"3.9\"", "mypy (>=1.14.1,<1.15.0) ; python_version == \"3.8\"", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.9.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"]
-fastai = ["fastai (>=2.4)", "fastcore (>=1.3.27)", "toml"]
-hf-transfer = ["hf-transfer (>=0.1.4)"]
-hf-xet = ["hf-xet (>=1.1.2,<2.0.0)"]
-inference = ["aiohttp"]
-mcp = ["aiohttp", "mcp (>=1.8.0)", "typer"]
-oauth = ["authlib (>=1.3.2)", "fastapi", "httpx", "itsdangerous"]
-quality = ["libcst (>=1.4.0)", "mypy (==1.15.0) ; python_version >= \"3.9\"", "mypy (>=1.14.1,<1.15.0) ; python_version == \"3.8\"", "ruff (>=0.9.0)"]
-tensorflow = ["graphviz", "pydot", "tensorflow"]
-tensorflow-testing = ["keras (<3.0)", "tensorflow"]
-testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "authlib (>=1.3.2)", "fastapi", "gradio (>=4.0.0)", "httpx", "itsdangerous", "jedi", "numpy", "pytest (>=8.1.1,<8.2.2)", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-mock", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"]
-torch = ["safetensors[torch]", "torch"]
-typing = ["types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)"]
-
-[[package]]
-name = "identify"
-version = "2.6.13"
-description = "File identification library for Python"
-optional = false
-python-versions = ">=3.9"
-groups = ["main"]
-files = [
- {file = "identify-2.6.13-py2.py3-none-any.whl", hash = "sha256:60381139b3ae39447482ecc406944190f690d4a2997f2584062089848361b33b"},
- {file = "identify-2.6.13.tar.gz", hash = "sha256:da8d6c828e773620e13bfa86ea601c5a5310ba4bcd65edf378198b56a1f9fb32"},
-]
-
-[package.extras]
-license = ["ukkonen"]
-
-[[package]]
-name = "idna"
-version = "3.10"
-description = "Internationalized Domain Names in Applications (IDNA)"
-optional = false
-python-versions = ">=3.6"
-groups = ["main", "dev", "xla"]
-files = [
- {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"},
- {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"},
-]
-
-[package.extras]
-all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"]
-
-[[package]]
-name = "iniconfig"
-version = "2.1.0"
-description = "brain-dead simple config-ini parsing"
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760"},
- {file = "iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7"},
-]
-
-[[package]]
-name = "ipykernel"
-version = "6.30.1"
-description = "IPython Kernel for Jupyter"
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "ipykernel-6.30.1-py3-none-any.whl", hash = "sha256:aa6b9fb93dca949069d8b85b6c79b2518e32ac583ae9c7d37c51d119e18b3fb4"},
- {file = "ipykernel-6.30.1.tar.gz", hash = "sha256:6abb270161896402e76b91394fcdce5d1be5d45f456671e5080572f8505be39b"},
-]
-
-[package.dependencies]
-appnope = {version = ">=0.1.2", markers = "platform_system == \"Darwin\""}
-comm = ">=0.1.1"
-debugpy = ">=1.6.5"
-ipython = ">=7.23.1"
-jupyter-client = ">=8.0.0"
-jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0"
-matplotlib-inline = ">=0.1"
-nest-asyncio = ">=1.4"
-packaging = ">=22"
-psutil = ">=5.7"
-pyzmq = ">=25"
-tornado = ">=6.2"
-traitlets = ">=5.4.0"
-
-[package.extras]
-cov = ["coverage[toml]", "matplotlib", "pytest-cov", "trio"]
-docs = ["intersphinx-registry", "myst-parser", "pydata-sphinx-theme", "sphinx", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "trio"]
-pyqt5 = ["pyqt5"]
-pyside6 = ["pyside6"]
-test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0,<9)", "pytest-asyncio (>=0.23.5)", "pytest-cov", "pytest-timeout"]
-
-[[package]]
-name = "ipython"
-version = "8.37.0"
-description = "IPython: Productive Interactive Computing"
-optional = false
-python-versions = ">=3.10"
-groups = ["dev"]
-markers = "python_version == \"3.10\""
-files = [
- {file = "ipython-8.37.0-py3-none-any.whl", hash = "sha256:ed87326596b878932dbcb171e3e698845434d8c61b8d8cd474bf663041a9dcf2"},
- {file = "ipython-8.37.0.tar.gz", hash = "sha256:ca815841e1a41a1e6b73a0b08f3038af9b2252564d01fc405356d34033012216"},
-]
-
-[package.dependencies]
-colorama = {version = "*", markers = "sys_platform == \"win32\""}
-decorator = "*"
-exceptiongroup = {version = "*", markers = "python_version < \"3.11\""}
-jedi = ">=0.16"
-matplotlib-inline = "*"
-pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""}
-prompt_toolkit = ">=3.0.41,<3.1.0"
-pygments = ">=2.4.0"
-stack_data = "*"
-traitlets = ">=5.13.0"
-typing_extensions = {version = ">=4.6", markers = "python_version < \"3.12\""}
-
-[package.extras]
-all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"]
-black = ["black"]
-doc = ["docrepr", "exceptiongroup", "intersphinx_registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "tomli ; python_version < \"3.11\"", "typing_extensions"]
-kernel = ["ipykernel"]
-matplotlib = ["matplotlib"]
-nbconvert = ["nbconvert"]
-nbformat = ["nbformat"]
-notebook = ["ipywidgets", "notebook"]
-parallel = ["ipyparallel"]
-qtconsole = ["qtconsole"]
-test = ["packaging", "pickleshare", "pytest", "pytest-asyncio (<0.22)", "testpath"]
-test-extra = ["curio", "ipython[test]", "jupyter_ai", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.23)", "pandas", "trio"]
-
-[[package]]
-name = "ipython"
-version = "9.5.0"
-description = "IPython: Productive Interactive Computing"
-optional = false
-python-versions = ">=3.11"
-groups = ["dev"]
-markers = "python_version >= \"3.11\""
-files = [
- {file = "ipython-9.5.0-py3-none-any.whl", hash = "sha256:88369ffa1d5817d609120daa523a6da06d02518e582347c29f8451732a9c5e72"},
- {file = "ipython-9.5.0.tar.gz", hash = "sha256:129c44b941fe6d9b82d36fc7a7c18127ddb1d6f02f78f867f402e2e3adde3113"},
-]
-
-[package.dependencies]
-colorama = {version = "*", markers = "sys_platform == \"win32\""}
-decorator = "*"
-ipython-pygments-lexers = "*"
-jedi = ">=0.16"
-matplotlib-inline = "*"
-pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""}
-prompt_toolkit = ">=3.0.41,<3.1.0"
-pygments = ">=2.4.0"
-stack_data = "*"
-traitlets = ">=5.13.0"
-typing_extensions = {version = ">=4.6", markers = "python_version < \"3.12\""}
-
-[package.extras]
-all = ["ipython[doc,matplotlib,test,test-extra]"]
-black = ["black"]
-doc = ["docrepr", "exceptiongroup", "intersphinx_registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinx_toml (==0.0.4)", "typing_extensions"]
-matplotlib = ["matplotlib"]
-test = ["packaging", "pytest", "pytest-asyncio", "testpath"]
-test-extra = ["curio", "ipykernel", "ipython[test]", "jupyter_ai", "matplotlib (!=3.2.0)", "nbclient", "nbformat", "numpy (>=1.23)", "pandas", "trio"]
-
-[[package]]
-name = "ipython-pygments-lexers"
-version = "1.1.1"
-description = "Defines a variety of Pygments lexers for highlighting IPython code."
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-markers = "python_version >= \"3.11\""
-files = [
- {file = "ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c"},
- {file = "ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81"},
-]
-
-[package.dependencies]
-pygments = "*"
-
-[[package]]
-name = "ipywidgets"
-version = "8.1.7"
-description = "Jupyter interactive widgets"
-optional = false
-python-versions = ">=3.7"
-groups = ["dev"]
-files = [
- {file = "ipywidgets-8.1.7-py3-none-any.whl", hash = "sha256:764f2602d25471c213919b8a1997df04bef869251db4ca8efba1b76b1bd9f7bb"},
- {file = "ipywidgets-8.1.7.tar.gz", hash = "sha256:15f1ac050b9ccbefd45dccfbb2ef6bed0029d8278682d569d71b8dd96bee0376"},
-]
-
-[package.dependencies]
-comm = ">=0.1.3"
-ipython = ">=6.1.0"
-jupyterlab_widgets = ">=3.0.15,<3.1.0"
-traitlets = ">=4.3.1"
-widgetsnbextension = ">=4.0.14,<4.1.0"
-
-[package.extras]
-test = ["ipykernel", "jsonschema", "pytest (>=3.6.0)", "pytest-cov", "pytz"]
-
-[[package]]
-name = "isoduration"
-version = "20.11.0"
-description = "Operations with ISO 8601 durations"
-optional = false
-python-versions = ">=3.7"
-groups = ["dev"]
-files = [
- {file = "isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042"},
- {file = "isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9"},
-]
-
-[package.dependencies]
-arrow = ">=0.15.0"
-
-[[package]]
-name = "jedi"
-version = "0.19.2"
-description = "An autocompletion tool for Python that can be used for text editors."
-optional = false
-python-versions = ">=3.6"
-groups = ["dev"]
-files = [
- {file = "jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9"},
- {file = "jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0"},
-]
-
-[package.dependencies]
-parso = ">=0.8.4,<0.9.0"
-
-[package.extras]
-docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"]
-qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"]
-testing = ["Django", "attrs", "colorama", "docopt", "pytest (<9.0.0)"]
-
-[[package]]
-name = "jinja2"
-version = "3.1.6"
-description = "A very fast and expressive template engine."
-optional = false
-python-versions = ">=3.7"
-groups = ["main", "dev"]
-files = [
- {file = "jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67"},
- {file = "jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d"},
-]
-
-[package.dependencies]
-MarkupSafe = ">=2.0"
-
-[package.extras]
-i18n = ["Babel (>=2.7)"]
-
-[[package]]
-name = "json5"
-version = "0.12.1"
-description = "A Python implementation of the JSON5 data format."
-optional = false
-python-versions = ">=3.8.0"
-groups = ["dev"]
-files = [
- {file = "json5-0.12.1-py3-none-any.whl", hash = "sha256:d9c9b3bc34a5f54d43c35e11ef7cb87d8bdd098c6ace87117a7b7e83e705c1d5"},
- {file = "json5-0.12.1.tar.gz", hash = "sha256:b2743e77b3242f8d03c143dd975a6ec7c52e2f2afe76ed934e53503dd4ad4990"},
-]
-
-[package.extras]
-dev = ["build (==1.2.2.post1)", "coverage (==7.5.4) ; python_version < \"3.9\"", "coverage (==7.8.0) ; python_version >= \"3.9\"", "mypy (==1.14.1) ; python_version < \"3.9\"", "mypy (==1.15.0) ; python_version >= \"3.9\"", "pip (==25.0.1)", "pylint (==3.2.7) ; python_version < \"3.9\"", "pylint (==3.3.6) ; python_version >= \"3.9\"", "ruff (==0.11.2)", "twine (==6.1.0)", "uv (==0.6.11)"]
-
-[[package]]
-name = "jsonpointer"
-version = "3.0.0"
-description = "Identify specific nodes in a JSON document (RFC 6901)"
-optional = false
-python-versions = ">=3.7"
-groups = ["dev"]
-files = [
- {file = "jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942"},
- {file = "jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef"},
-]
-
-[[package]]
-name = "jsonschema"
-version = "4.25.1"
-description = "An implementation of JSON Schema validation for Python"
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "jsonschema-4.25.1-py3-none-any.whl", hash = "sha256:3fba0169e345c7175110351d456342c364814cfcf3b964ba4587f22915230a63"},
- {file = "jsonschema-4.25.1.tar.gz", hash = "sha256:e4a9655ce0da0c0b67a085847e00a3a51449e1157f4f75e9fb5aa545e122eb85"},
-]
-
-[package.dependencies]
-attrs = ">=22.2.0"
-fqdn = {version = "*", optional = true, markers = "extra == \"format-nongpl\""}
-idna = {version = "*", optional = true, markers = "extra == \"format-nongpl\""}
-isoduration = {version = "*", optional = true, markers = "extra == \"format-nongpl\""}
-jsonpointer = {version = ">1.13", optional = true, markers = "extra == \"format-nongpl\""}
-jsonschema-specifications = ">=2023.03.6"
-referencing = ">=0.28.4"
-rfc3339-validator = {version = "*", optional = true, markers = "extra == \"format-nongpl\""}
-rfc3986-validator = {version = ">0.1.0", optional = true, markers = "extra == \"format-nongpl\""}
-rfc3987-syntax = {version = ">=1.1.0", optional = true, markers = "extra == \"format-nongpl\""}
-rpds-py = ">=0.7.1"
-uri-template = {version = "*", optional = true, markers = "extra == \"format-nongpl\""}
-webcolors = {version = ">=24.6.0", optional = true, markers = "extra == \"format-nongpl\""}
-
-[package.extras]
-format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"]
-format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "rfc3987-syntax (>=1.1.0)", "uri-template", "webcolors (>=24.6.0)"]
-
-[[package]]
-name = "jsonschema-specifications"
-version = "2025.4.1"
-description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry"
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "jsonschema_specifications-2025.4.1-py3-none-any.whl", hash = "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af"},
- {file = "jsonschema_specifications-2025.4.1.tar.gz", hash = "sha256:630159c9f4dbea161a6a2205c3011cc4f18ff381b189fff48bb39b9bf26ae608"},
-]
-
-[package.dependencies]
-referencing = ">=0.31.0"
-
-[[package]]
-name = "jupyter"
-version = "1.1.1"
-description = "Jupyter metapackage. Install all the Jupyter components in one go."
-optional = false
-python-versions = "*"
-groups = ["dev"]
-files = [
- {file = "jupyter-1.1.1-py2.py3-none-any.whl", hash = "sha256:7a59533c22af65439b24bbe60373a4e95af8f16ac65a6c00820ad378e3f7cc83"},
- {file = "jupyter-1.1.1.tar.gz", hash = "sha256:d55467bceabdea49d7e3624af7e33d59c37fff53ed3a350e1ac957bed731de7a"},
-]
-
-[package.dependencies]
-ipykernel = "*"
-ipywidgets = "*"
-jupyter-console = "*"
-jupyterlab = "*"
-nbconvert = "*"
-notebook = "*"
-
-[[package]]
-name = "jupyter-client"
-version = "8.6.3"
-description = "Jupyter protocol implementation and client libraries"
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "jupyter_client-8.6.3-py3-none-any.whl", hash = "sha256:e8a19cc986cc45905ac3362915f410f3af85424b4c0905e94fa5f2cb08e8f23f"},
- {file = "jupyter_client-8.6.3.tar.gz", hash = "sha256:35b3a0947c4a6e9d589eb97d7d4cd5e90f910ee73101611f01283732bd6d9419"},
-]
-
-[package.dependencies]
-jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0"
-python-dateutil = ">=2.8.2"
-pyzmq = ">=23.0"
-tornado = ">=6.2"
-traitlets = ">=5.3"
-
-[package.extras]
-docs = ["ipykernel", "myst-parser", "pydata-sphinx-theme", "sphinx (>=4)", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"]
-test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko ; sys_platform == \"win32\"", "pre-commit", "pytest (<8.2.0)", "pytest-cov", "pytest-jupyter[client] (>=0.4.1)", "pytest-timeout"]
-
-[[package]]
-name = "jupyter-console"
-version = "6.6.3"
-description = "Jupyter terminal console"
-optional = false
-python-versions = ">=3.7"
-groups = ["dev"]
-files = [
- {file = "jupyter_console-6.6.3-py3-none-any.whl", hash = "sha256:309d33409fcc92ffdad25f0bcdf9a4a9daa61b6f341177570fdac03de5352485"},
- {file = "jupyter_console-6.6.3.tar.gz", hash = "sha256:566a4bf31c87adbfadf22cdf846e3069b59a71ed5da71d6ba4d8aaad14a53539"},
-]
-
-[package.dependencies]
-ipykernel = ">=6.14"
-ipython = "*"
-jupyter-client = ">=7.0.0"
-jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0"
-prompt-toolkit = ">=3.0.30"
-pygments = "*"
-pyzmq = ">=17"
-traitlets = ">=5.4"
-
-[package.extras]
-test = ["flaky", "pexpect", "pytest"]
-
-[[package]]
-name = "jupyter-core"
-version = "5.8.1"
-description = "Jupyter core package. A base package on which Jupyter projects rely."
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "jupyter_core-5.8.1-py3-none-any.whl", hash = "sha256:c28d268fc90fb53f1338ded2eb410704c5449a358406e8a948b75706e24863d0"},
- {file = "jupyter_core-5.8.1.tar.gz", hash = "sha256:0a5f9706f70e64786b75acba995988915ebd4601c8a52e534a40b51c95f59941"},
-]
-
-[package.dependencies]
-platformdirs = ">=2.5"
-pywin32 = {version = ">=300", markers = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\""}
-traitlets = ">=5.3"
-
-[package.extras]
-docs = ["intersphinx-registry", "myst-parser", "pydata-sphinx-theme", "sphinx-autodoc-typehints", "sphinxcontrib-spelling", "traitlets"]
-test = ["ipykernel", "pre-commit", "pytest (<9)", "pytest-cov", "pytest-timeout"]
-
-[[package]]
-name = "jupyter-events"
-version = "0.12.0"
-description = "Jupyter Event System library"
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "jupyter_events-0.12.0-py3-none-any.whl", hash = "sha256:6464b2fa5ad10451c3d35fabc75eab39556ae1e2853ad0c0cc31b656731a97fb"},
- {file = "jupyter_events-0.12.0.tar.gz", hash = "sha256:fc3fce98865f6784c9cd0a56a20644fc6098f21c8c33834a8d9fe383c17e554b"},
-]
-
-[package.dependencies]
-jsonschema = {version = ">=4.18.0", extras = ["format-nongpl"]}
-packaging = "*"
-python-json-logger = ">=2.0.4"
-pyyaml = ">=5.3"
-referencing = "*"
-rfc3339-validator = "*"
-rfc3986-validator = ">=0.1.1"
-traitlets = ">=5.3"
-
-[package.extras]
-cli = ["click", "rich"]
-docs = ["jupyterlite-sphinx", "myst-parser", "pydata-sphinx-theme (>=0.16)", "sphinx (>=8)", "sphinxcontrib-spelling"]
-test = ["click", "pre-commit", "pytest (>=7.0)", "pytest-asyncio (>=0.19.0)", "pytest-console-scripts", "rich"]
-
-[[package]]
-name = "jupyter-lsp"
-version = "2.3.0"
-description = "Multi-Language Server WebSocket proxy for Jupyter Notebook/Lab server"
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "jupyter_lsp-2.3.0-py3-none-any.whl", hash = "sha256:e914a3cb2addf48b1c7710914771aaf1819d46b2e5a79b0f917b5478ec93f34f"},
- {file = "jupyter_lsp-2.3.0.tar.gz", hash = "sha256:458aa59339dc868fb784d73364f17dbce8836e906cd75fd471a325cba02e0245"},
-]
-
-[package.dependencies]
-jupyter_server = ">=1.1.2"
-
-[[package]]
-name = "jupyter-server"
-version = "2.17.0"
-description = "The backend—i.e. core services, APIs, and REST endpoints—to Jupyter web applications."
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "jupyter_server-2.17.0-py3-none-any.whl", hash = "sha256:e8cb9c7db4251f51ed307e329b81b72ccf2056ff82d50524debde1ee1870e13f"},
- {file = "jupyter_server-2.17.0.tar.gz", hash = "sha256:c38ea898566964c888b4772ae1ed58eca84592e88251d2cfc4d171f81f7e99d5"},
-]
-
-[package.dependencies]
-anyio = ">=3.1.0"
-argon2-cffi = ">=21.1"
-jinja2 = ">=3.0.3"
-jupyter-client = ">=7.4.4"
-jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0"
-jupyter-events = ">=0.11.0"
-jupyter-server-terminals = ">=0.4.4"
-nbconvert = ">=6.4.4"
-nbformat = ">=5.3.0"
-overrides = {version = ">=5.0", markers = "python_version < \"3.12\""}
-packaging = ">=22.0"
-prometheus-client = ">=0.9"
-pywinpty = {version = ">=2.0.1", markers = "os_name == \"nt\""}
-pyzmq = ">=24"
-send2trash = ">=1.8.2"
-terminado = ">=0.8.3"
-tornado = ">=6.2.0"
-traitlets = ">=5.6.0"
-websocket-client = ">=1.7"
-
-[package.extras]
-docs = ["ipykernel", "jinja2", "jupyter-client", "myst-parser", "nbformat", "prometheus-client", "pydata-sphinx-theme", "send2trash", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-openapi (>=0.8.0)", "sphinxcontrib-spelling", "sphinxemoji", "tornado", "typing-extensions"]
-test = ["flaky", "ipykernel", "pre-commit", "pytest (>=7.0,<9)", "pytest-console-scripts", "pytest-jupyter[server] (>=0.7)", "pytest-timeout", "requests"]
-
-[[package]]
-name = "jupyter-server-terminals"
-version = "0.5.3"
-description = "A Jupyter Server Extension Providing Terminals."
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "jupyter_server_terminals-0.5.3-py3-none-any.whl", hash = "sha256:41ee0d7dc0ebf2809c668e0fc726dfaf258fcd3e769568996ca731b6194ae9aa"},
- {file = "jupyter_server_terminals-0.5.3.tar.gz", hash = "sha256:5ae0295167220e9ace0edcfdb212afd2b01ee8d179fe6f23c899590e9b8a5269"},
-]
-
-[package.dependencies]
-pywinpty = {version = ">=2.0.3", markers = "os_name == \"nt\""}
-terminado = ">=0.8.3"
-
-[package.extras]
-docs = ["jinja2", "jupyter-server", "mistune (<4.0)", "myst-parser", "nbformat", "packaging", "pydata-sphinx-theme", "sphinxcontrib-github-alt", "sphinxcontrib-openapi", "sphinxcontrib-spelling", "sphinxemoji", "tornado"]
-test = ["jupyter-server (>=2.0.0)", "pytest (>=7.0)", "pytest-jupyter[server] (>=0.5.3)", "pytest-timeout"]
-
-[[package]]
-name = "jupyterlab"
-version = "4.4.7"
-description = "JupyterLab computational environment"
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "jupyterlab-4.4.7-py3-none-any.whl", hash = "sha256:808bae6136b507a4d18f04254218bfe71ed8ba399a36ef3280d5f259e69abf80"},
- {file = "jupyterlab-4.4.7.tar.gz", hash = "sha256:8c8e225492f4513ebde9bbbc00a05b651ab9a1f5b0013015d96fabf671c37188"},
-]
-
-[package.dependencies]
-async-lru = ">=1.0.0"
-httpx = ">=0.25.0,<1"
-ipykernel = ">=6.5.0,<6.30.0 || >6.30.0"
-jinja2 = ">=3.0.3"
-jupyter-core = "*"
-jupyter-lsp = ">=2.0.0"
-jupyter-server = ">=2.4.0,<3"
-jupyterlab-server = ">=2.27.1,<3"
-notebook-shim = ">=0.2"
-packaging = "*"
-setuptools = ">=41.1.0"
-tomli = {version = ">=1.2.2", markers = "python_version < \"3.11\""}
-tornado = ">=6.2.0"
-traitlets = "*"
-
-[package.extras]
-dev = ["build", "bump2version", "coverage", "hatch", "pre-commit", "pytest-cov", "ruff (==0.11.4)"]
-docs = ["jsx-lexer", "myst-parser", "pydata-sphinx-theme (>=0.13.0)", "pytest", "pytest-check-links", "pytest-jupyter", "sphinx (>=1.8,<8.2.0)", "sphinx-copybutton"]
-docs-screenshots = ["altair (==5.5.0)", "ipython (==8.16.1)", "ipywidgets (==8.1.5)", "jupyterlab-geojson (==3.4.0)", "jupyterlab-language-pack-zh-cn (==4.3.post1)", "matplotlib (==3.10.0)", "nbconvert (>=7.0.0)", "pandas (==2.2.3)", "scipy (==1.15.1)", "vega-datasets (==0.9.0)"]
-test = ["coverage", "pytest (>=7.0)", "pytest-check-links (>=0.7)", "pytest-console-scripts", "pytest-cov", "pytest-jupyter (>=0.5.3)", "pytest-timeout", "pytest-tornasync", "requests", "requests-cache", "virtualenv"]
-upgrade-extension = ["copier (>=9,<10)", "jinja2-time (<0.3)", "pydantic (<3.0)", "pyyaml-include (<3.0)", "tomli-w (<2.0)"]
-
-[[package]]
-name = "jupyterlab-pygments"
-version = "0.3.0"
-description = "Pygments theme using JupyterLab CSS variables"
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780"},
- {file = "jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d"},
-]
-
-[[package]]
-name = "jupyterlab-server"
-version = "2.27.3"
-description = "A set of server components for JupyterLab and JupyterLab like applications."
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "jupyterlab_server-2.27.3-py3-none-any.whl", hash = "sha256:e697488f66c3db49df675158a77b3b017520d772c6e1548c7d9bcc5df7944ee4"},
- {file = "jupyterlab_server-2.27.3.tar.gz", hash = "sha256:eb36caca59e74471988f0ae25c77945610b887f777255aa21f8065def9e51ed4"},
-]
-
-[package.dependencies]
-babel = ">=2.10"
-jinja2 = ">=3.0.3"
-json5 = ">=0.9.0"
-jsonschema = ">=4.18.0"
-jupyter-server = ">=1.21,<3"
-packaging = ">=21.3"
-requests = ">=2.31"
-
-[package.extras]
-docs = ["autodoc-traits", "jinja2 (<3.2.0)", "mistune (<4)", "myst-parser", "pydata-sphinx-theme", "sphinx", "sphinx-copybutton", "sphinxcontrib-openapi (>0.8)"]
-openapi = ["openapi-core (>=0.18.0,<0.19.0)", "ruamel-yaml"]
-test = ["hatch", "ipykernel", "openapi-core (>=0.18.0,<0.19.0)", "openapi-spec-validator (>=0.6.0,<0.8.0)", "pytest (>=7.0,<8)", "pytest-console-scripts", "pytest-cov", "pytest-jupyter[server] (>=0.6.2)", "pytest-timeout", "requests-mock", "ruamel-yaml", "sphinxcontrib-spelling", "strict-rfc3339", "werkzeug"]
-
-[[package]]
-name = "jupyterlab-widgets"
-version = "3.0.15"
-description = "Jupyter interactive widgets for JupyterLab"
-optional = false
-python-versions = ">=3.7"
-groups = ["dev"]
-files = [
- {file = "jupyterlab_widgets-3.0.15-py3-none-any.whl", hash = "sha256:d59023d7d7ef71400d51e6fee9a88867f6e65e10a4201605d2d7f3e8f012a31c"},
- {file = "jupyterlab_widgets-3.0.15.tar.gz", hash = "sha256:2920888a0c2922351a9202817957a68c07d99673504d6cd37345299e971bb08b"},
-]
-
-[[package]]
-name = "lark"
-version = "1.2.2"
-description = "a modern parsing library"
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "lark-1.2.2-py3-none-any.whl", hash = "sha256:c2276486b02f0f1b90be155f2c8ba4a8e194d42775786db622faccd652d8e80c"},
- {file = "lark-1.2.2.tar.gz", hash = "sha256:ca807d0162cd16cef15a8feecb862d7319e7a09bdb13aef927968e45040fed80"},
-]
-
-[package.extras]
-atomic-cache = ["atomicwrites"]
-interegular = ["interegular (>=0.3.1,<0.4.0)"]
-nearley = ["js2py"]
-regex = ["regex"]
-
-[[package]]
-name = "libtpu"
-version = "0.0.17"
-description = "libtpu is the core library that enables machine learning frameworks like JAX, PyTorch, and TensorFlow to execute models on Google Cloud TPUs. It provides core functionality for compilation, inter-chip communication (ICI), and runtime execution, and includes a set of SDK primitives for direct TPU interaction and deployment. Learn more about Cloud TPUs at https://cloud.google.com/tpu."
-optional = false
-python-versions = "*"
-groups = ["xla"]
-files = [
- {file = "libtpu-0.0.17-py3-none-manylinux_2_31_x86_64.whl", hash = "sha256:4963214199e151613874c2b3f45b983bb09a6e4ba43bb028c917323b8caf418b"},
-]
-
-[[package]]
-name = "markdown-it-py"
-version = "4.0.0"
-description = "Python port of markdown-it. Markdown parsing, done right!"
-optional = false
-python-versions = ">=3.10"
-groups = ["xla"]
-files = [
- {file = "markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147"},
- {file = "markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3"},
-]
-
-[package.dependencies]
-mdurl = ">=0.1,<1.0"
-
-[package.extras]
-benchmarking = ["psutil", "pytest", "pytest-benchmark"]
-compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "markdown-it-pyrs", "mistletoe (>=1.0,<2.0)", "mistune (>=3.0,<4.0)", "panflute (>=2.3,<3.0)"]
-linkify = ["linkify-it-py (>=1,<3)"]
-plugins = ["mdit-py-plugins (>=0.5.0)"]
-profiling = ["gprof2dot"]
-rtd = ["ipykernel", "jupyter_sphinx", "mdit-py-plugins (>=0.5.0)", "myst-parser", "pyyaml", "sphinx", "sphinx-book-theme (>=1.0,<2.0)", "sphinx-copybutton", "sphinx-design"]
-testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions", "requests"]
-
-[[package]]
-name = "markupsafe"
-version = "3.0.2"
-description = "Safely add untrusted strings to HTML/XML markup."
-optional = false
-python-versions = ">=3.9"
-groups = ["main", "dev"]
-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"},
- {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"},
- {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"},
- {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"},
- {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"},
- {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"},
- {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"},
- {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"},
- {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"},
- {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"},
- {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"},
- {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"},
- {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"},
- {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"},
- {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"},
- {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"},
- {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"},
- {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"},
- {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"},
- {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"},
- {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"},
- {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"},
- {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"},
- {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"},
- {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"},
- {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"},
- {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"},
- {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"},
- {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"},
- {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"},
- {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"},
- {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"},
- {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"},
- {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"},
- {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"},
- {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"},
- {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"},
- {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"},
- {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"},
- {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"},
- {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"},
- {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"},
- {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"},
- {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"},
- {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"},
- {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"},
- {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"},
- {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"},
- {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"},
- {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"},
- {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"},
- {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"},
- {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"},
- {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"},
- {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"},
- {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"},
- {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"},
- {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"},
- {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"},
- {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"},
-]
-
-[[package]]
-name = "matplotlib-inline"
-version = "0.1.7"
-description = "Inline Matplotlib backend for Jupyter"
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"},
- {file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"},
-]
-
-[package.dependencies]
-traitlets = "*"
-
-[[package]]
-name = "mdurl"
-version = "0.1.2"
-description = "Markdown URL utilities"
-optional = false
-python-versions = ">=3.7"
-groups = ["xla"]
-files = [
- {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"},
- {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
-]
-
-[[package]]
-name = "mistune"
-version = "3.1.4"
-description = "A sane and fast Markdown parser with useful plugins and renderers"
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "mistune-3.1.4-py3-none-any.whl", hash = "sha256:93691da911e5d9d2e23bc54472892aff676df27a75274962ff9edc210364266d"},
- {file = "mistune-3.1.4.tar.gz", hash = "sha256:b5a7f801d389f724ec702840c11d8fc48f2b33519102fc7ee739e8177b672164"},
-]
-
-[package.dependencies]
-typing-extensions = {version = "*", markers = "python_version < \"3.11\""}
-
-[[package]]
-name = "mpmath"
-version = "1.3.0"
-description = "Python library for arbitrary-precision floating-point arithmetic"
-optional = false
-python-versions = "*"
-groups = ["main"]
-files = [
- {file = "mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c"},
- {file = "mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f"},
-]
-
-[package.extras]
-develop = ["codecov", "pycodestyle", "pytest (>=4.6)", "pytest-cov", "wheel"]
-docs = ["sphinx"]
-gmpy = ["gmpy2 (>=2.1.0a4) ; platform_python_implementation != \"PyPy\""]
-tests = ["pytest (>=4.6)"]
-
-[[package]]
-name = "multidict"
-version = "6.6.4"
-description = "multidict implementation"
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "multidict-6.6.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b8aa6f0bd8125ddd04a6593437bad6a7e70f300ff4180a531654aa2ab3f6d58f"},
- {file = "multidict-6.6.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b9e5853bbd7264baca42ffc53391b490d65fe62849bf2c690fa3f6273dbcd0cb"},
- {file = "multidict-6.6.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0af5f9dee472371e36d6ae38bde009bd8ce65ac7335f55dcc240379d7bed1495"},
- {file = "multidict-6.6.4-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:d24f351e4d759f5054b641c81e8291e5d122af0fca5c72454ff77f7cbe492de8"},
- {file = "multidict-6.6.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:db6a3810eec08280a172a6cd541ff4a5f6a97b161d93ec94e6c4018917deb6b7"},
- {file = "multidict-6.6.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a1b20a9d56b2d81e2ff52ecc0670d583eaabaa55f402e8d16dd062373dbbe796"},
- {file = "multidict-6.6.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8c9854df0eaa610a23494c32a6f44a3a550fb398b6b51a56e8c6b9b3689578db"},
- {file = "multidict-6.6.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4bb7627fd7a968f41905a4d6343b0d63244a0623f006e9ed989fa2b78f4438a0"},
- {file = "multidict-6.6.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:caebafea30ed049c57c673d0b36238b1748683be2593965614d7b0e99125c877"},
- {file = "multidict-6.6.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:ad887a8250eb47d3ab083d2f98db7f48098d13d42eb7a3b67d8a5c795f224ace"},
- {file = "multidict-6.6.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:ed8358ae7d94ffb7c397cecb62cbac9578a83ecefc1eba27b9090ee910e2efb6"},
- {file = "multidict-6.6.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ecab51ad2462197a4c000b6d5701fc8585b80eecb90583635d7e327b7b6923eb"},
- {file = "multidict-6.6.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:c5c97aa666cf70e667dfa5af945424ba1329af5dd988a437efeb3a09430389fb"},
- {file = "multidict-6.6.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:9a950b7cf54099c1209f455ac5970b1ea81410f2af60ed9eb3c3f14f0bfcf987"},
- {file = "multidict-6.6.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:163c7ea522ea9365a8a57832dea7618e6cbdc3cd75f8c627663587459a4e328f"},
- {file = "multidict-6.6.4-cp310-cp310-win32.whl", hash = "sha256:17d2cbbfa6ff20821396b25890f155f40c986f9cfbce5667759696d83504954f"},
- {file = "multidict-6.6.4-cp310-cp310-win_amd64.whl", hash = "sha256:ce9a40fbe52e57e7edf20113a4eaddfacac0561a0879734e636aa6d4bb5e3fb0"},
- {file = "multidict-6.6.4-cp310-cp310-win_arm64.whl", hash = "sha256:01d0959807a451fe9fdd4da3e139cb5b77f7328baf2140feeaf233e1d777b729"},
- {file = "multidict-6.6.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c7a0e9b561e6460484318a7612e725df1145d46b0ef57c6b9866441bf6e27e0c"},
- {file = "multidict-6.6.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6bf2f10f70acc7a2446965ffbc726e5fc0b272c97a90b485857e5c70022213eb"},
- {file = "multidict-6.6.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:66247d72ed62d5dd29752ffc1d3b88f135c6a8de8b5f63b7c14e973ef5bda19e"},
- {file = "multidict-6.6.4-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:105245cc6b76f51e408451a844a54e6823bbd5a490ebfe5bdfc79798511ceded"},
- {file = "multidict-6.6.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cbbc54e58b34c3bae389ef00046be0961f30fef7cb0dd9c7756aee376a4f7683"},
- {file = "multidict-6.6.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:56c6b3652f945c9bc3ac6c8178cd93132b8d82dd581fcbc3a00676c51302bc1a"},
- {file = "multidict-6.6.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b95494daf857602eccf4c18ca33337dd2be705bccdb6dddbfc9d513e6addb9d9"},
- {file = "multidict-6.6.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e5b1413361cef15340ab9dc61523e653d25723e82d488ef7d60a12878227ed50"},
- {file = "multidict-6.6.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e167bf899c3d724f9662ef00b4f7fef87a19c22b2fead198a6f68b263618df52"},
- {file = "multidict-6.6.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:aaea28ba20a9026dfa77f4b80369e51cb767c61e33a2d4043399c67bd95fb7c6"},
- {file = "multidict-6.6.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:8c91cdb30809a96d9ecf442ec9bc45e8cfaa0f7f8bdf534e082c2443a196727e"},
- {file = "multidict-6.6.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1a0ccbfe93ca114c5d65a2471d52d8829e56d467c97b0e341cf5ee45410033b3"},
- {file = "multidict-6.6.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:55624b3f321d84c403cb7d8e6e982f41ae233d85f85db54ba6286f7295dc8a9c"},
- {file = "multidict-6.6.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:4a1fb393a2c9d202cb766c76208bd7945bc194eba8ac920ce98c6e458f0b524b"},
- {file = "multidict-6.6.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:43868297a5759a845fa3a483fb4392973a95fb1de891605a3728130c52b8f40f"},
- {file = "multidict-6.6.4-cp311-cp311-win32.whl", hash = "sha256:ed3b94c5e362a8a84d69642dbeac615452e8af9b8eb825b7bc9f31a53a1051e2"},
- {file = "multidict-6.6.4-cp311-cp311-win_amd64.whl", hash = "sha256:d8c112f7a90d8ca5d20213aa41eac690bb50a76da153e3afb3886418e61cb22e"},
- {file = "multidict-6.6.4-cp311-cp311-win_arm64.whl", hash = "sha256:3bb0eae408fa1996d87247ca0d6a57b7fc1dcf83e8a5c47ab82c558c250d4adf"},
- {file = "multidict-6.6.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0ffb87be160942d56d7b87b0fdf098e81ed565add09eaa1294268c7f3caac4c8"},
- {file = "multidict-6.6.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:d191de6cbab2aff5de6c5723101705fd044b3e4c7cfd587a1929b5028b9714b3"},
- {file = "multidict-6.6.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:38a0956dd92d918ad5feff3db8fcb4a5eb7dba114da917e1a88475619781b57b"},
- {file = "multidict-6.6.4-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:6865f6d3b7900ae020b495d599fcf3765653bc927951c1abb959017f81ae8287"},
- {file = "multidict-6.6.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0a2088c126b6f72db6c9212ad827d0ba088c01d951cee25e758c450da732c138"},
- {file = "multidict-6.6.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0f37bed7319b848097085d7d48116f545985db988e2256b2e6f00563a3416ee6"},
- {file = "multidict-6.6.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:01368e3c94032ba6ca0b78e7ccb099643466cf24f8dc8eefcfdc0571d56e58f9"},
- {file = "multidict-6.6.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fe323540c255db0bffee79ad7f048c909f2ab0edb87a597e1c17da6a54e493c"},
- {file = "multidict-6.6.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b8eb3025f17b0a4c3cd08cda49acf312a19ad6e8a4edd9dbd591e6506d999402"},
- {file = "multidict-6.6.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bbc14f0365534d35a06970d6a83478b249752e922d662dc24d489af1aa0d1be7"},
- {file = "multidict-6.6.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:75aa52fba2d96bf972e85451b99d8e19cc37ce26fd016f6d4aa60da9ab2b005f"},
- {file = "multidict-6.6.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4fefd4a815e362d4f011919d97d7b4a1e566f1dde83dc4ad8cfb5b41de1df68d"},
- {file = "multidict-6.6.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:db9801fe021f59a5b375ab778973127ca0ac52429a26e2fd86aa9508f4d26eb7"},
- {file = "multidict-6.6.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a650629970fa21ac1fb06ba25dabfc5b8a2054fcbf6ae97c758aa956b8dba802"},
- {file = "multidict-6.6.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:452ff5da78d4720d7516a3a2abd804957532dd69296cb77319c193e3ffb87e24"},
- {file = "multidict-6.6.4-cp312-cp312-win32.whl", hash = "sha256:8c2fcb12136530ed19572bbba61b407f655e3953ba669b96a35036a11a485793"},
- {file = "multidict-6.6.4-cp312-cp312-win_amd64.whl", hash = "sha256:047d9425860a8c9544fed1b9584f0c8bcd31bcde9568b047c5e567a1025ecd6e"},
- {file = "multidict-6.6.4-cp312-cp312-win_arm64.whl", hash = "sha256:14754eb72feaa1e8ae528468f24250dd997b8e2188c3d2f593f9eba259e4b364"},
- {file = "multidict-6.6.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f46a6e8597f9bd71b31cc708195d42b634c8527fecbcf93febf1052cacc1f16e"},
- {file = "multidict-6.6.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:22e38b2bc176c5eb9c0a0e379f9d188ae4cd8b28c0f53b52bce7ab0a9e534657"},
- {file = "multidict-6.6.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5df8afd26f162da59e218ac0eefaa01b01b2e6cd606cffa46608f699539246da"},
- {file = "multidict-6.6.4-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:49517449b58d043023720aa58e62b2f74ce9b28f740a0b5d33971149553d72aa"},
- {file = "multidict-6.6.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ae9408439537c5afdca05edd128a63f56a62680f4b3c234301055d7a2000220f"},
- {file = "multidict-6.6.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:87a32d20759dc52a9e850fe1061b6e41ab28e2998d44168a8a341b99ded1dba0"},
- {file = "multidict-6.6.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:52e3c8d43cdfff587ceedce9deb25e6ae77daba560b626e97a56ddcad3756879"},
- {file = "multidict-6.6.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ad8850921d3a8d8ff6fbef790e773cecfc260bbfa0566998980d3fa8f520bc4a"},
- {file = "multidict-6.6.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:497a2954adc25c08daff36f795077f63ad33e13f19bfff7736e72c785391534f"},
- {file = "multidict-6.6.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:024ce601f92d780ca1617ad4be5ac15b501cc2414970ffa2bb2bbc2bd5a68fa5"},
- {file = "multidict-6.6.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:a693fc5ed9bdd1c9e898013e0da4dcc640de7963a371c0bd458e50e046bf6438"},
- {file = "multidict-6.6.4-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:190766dac95aab54cae5b152a56520fd99298f32a1266d66d27fdd1b5ac00f4e"},
- {file = "multidict-6.6.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:34d8f2a5ffdceab9dcd97c7a016deb2308531d5f0fced2bb0c9e1df45b3363d7"},
- {file = "multidict-6.6.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:59e8d40ab1f5a8597abcef00d04845155a5693b5da00d2c93dbe88f2050f2812"},
- {file = "multidict-6.6.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:467fe64138cfac771f0e949b938c2e1ada2b5af22f39692aa9258715e9ea613a"},
- {file = "multidict-6.6.4-cp313-cp313-win32.whl", hash = "sha256:14616a30fe6d0a48d0a48d1a633ab3b8bec4cf293aac65f32ed116f620adfd69"},
- {file = "multidict-6.6.4-cp313-cp313-win_amd64.whl", hash = "sha256:40cd05eaeb39e2bc8939451f033e57feaa2ac99e07dbca8afe2be450a4a3b6cf"},
- {file = "multidict-6.6.4-cp313-cp313-win_arm64.whl", hash = "sha256:f6eb37d511bfae9e13e82cb4d1af36b91150466f24d9b2b8a9785816deb16605"},
- {file = "multidict-6.6.4-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:6c84378acd4f37d1b507dfa0d459b449e2321b3ba5f2338f9b085cf7a7ba95eb"},
- {file = "multidict-6.6.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0e0558693063c75f3d952abf645c78f3c5dfdd825a41d8c4d8156fc0b0da6e7e"},
- {file = "multidict-6.6.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3f8e2384cb83ebd23fd07e9eada8ba64afc4c759cd94817433ab8c81ee4b403f"},
- {file = "multidict-6.6.4-cp313-cp313t-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:f996b87b420995a9174b2a7c1a8daf7db4750be6848b03eb5e639674f7963773"},
- {file = "multidict-6.6.4-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cc356250cffd6e78416cf5b40dc6a74f1edf3be8e834cf8862d9ed5265cf9b0e"},
- {file = "multidict-6.6.4-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:dadf95aa862714ea468a49ad1e09fe00fcc9ec67d122f6596a8d40caf6cec7d0"},
- {file = "multidict-6.6.4-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7dd57515bebffd8ebd714d101d4c434063322e4fe24042e90ced41f18b6d3395"},
- {file = "multidict-6.6.4-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:967af5f238ebc2eb1da4e77af5492219fbd9b4b812347da39a7b5f5c72c0fa45"},
- {file = "multidict-6.6.4-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2a4c6875c37aae9794308ec43e3530e4aa0d36579ce38d89979bbf89582002bb"},
- {file = "multidict-6.6.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:7f683a551e92bdb7fac545b9c6f9fa2aebdeefa61d607510b3533286fcab67f5"},
- {file = "multidict-6.6.4-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:3ba5aaf600edaf2a868a391779f7a85d93bed147854925f34edd24cc70a3e141"},
- {file = "multidict-6.6.4-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:580b643b7fd2c295d83cad90d78419081f53fd532d1f1eb67ceb7060f61cff0d"},
- {file = "multidict-6.6.4-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:37b7187197da6af3ee0b044dbc9625afd0c885f2800815b228a0e70f9a7f473d"},
- {file = "multidict-6.6.4-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e1b93790ed0bc26feb72e2f08299691ceb6da5e9e14a0d13cc74f1869af327a0"},
- {file = "multidict-6.6.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:a506a77ddee1efcca81ecbeae27ade3e09cdf21a8ae854d766c2bb4f14053f92"},
- {file = "multidict-6.6.4-cp313-cp313t-win32.whl", hash = "sha256:f93b2b2279883d1d0a9e1bd01f312d6fc315c5e4c1f09e112e4736e2f650bc4e"},
- {file = "multidict-6.6.4-cp313-cp313t-win_amd64.whl", hash = "sha256:6d46a180acdf6e87cc41dc15d8f5c2986e1e8739dc25dbb7dac826731ef381a4"},
- {file = "multidict-6.6.4-cp313-cp313t-win_arm64.whl", hash = "sha256:756989334015e3335d087a27331659820d53ba432befdef6a718398b0a8493ad"},
- {file = "multidict-6.6.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:af7618b591bae552b40dbb6f93f5518328a949dac626ee75927bba1ecdeea9f4"},
- {file = "multidict-6.6.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b6819f83aef06f560cb15482d619d0e623ce9bf155115150a85ab11b8342a665"},
- {file = "multidict-6.6.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4d09384e75788861e046330308e7af54dd306aaf20eb760eb1d0de26b2bea2cb"},
- {file = "multidict-6.6.4-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:a59c63061f1a07b861c004e53869eb1211ffd1a4acbca330e3322efa6dd02978"},
- {file = "multidict-6.6.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:350f6b0fe1ced61e778037fdc7613f4051c8baf64b1ee19371b42a3acdb016a0"},
- {file = "multidict-6.6.4-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0c5cbac6b55ad69cb6aa17ee9343dfbba903118fd530348c330211dc7aa756d1"},
- {file = "multidict-6.6.4-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:630f70c32b8066ddfd920350bc236225814ad94dfa493fe1910ee17fe4365cbb"},
- {file = "multidict-6.6.4-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f8d4916a81697faec6cb724a273bd5457e4c6c43d82b29f9dc02c5542fd21fc9"},
- {file = "multidict-6.6.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e42332cf8276bb7645d310cdecca93a16920256a5b01bebf747365f86a1675b"},
- {file = "multidict-6.6.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f3be27440f7644ab9a13a6fc86f09cdd90b347c3c5e30c6d6d860de822d7cb53"},
- {file = "multidict-6.6.4-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:21f216669109e02ef3e2415ede07f4f8987f00de8cdfa0cc0b3440d42534f9f0"},
- {file = "multidict-6.6.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:d9890d68c45d1aeac5178ded1d1cccf3bc8d7accf1f976f79bf63099fb16e4bd"},
- {file = "multidict-6.6.4-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:edfdcae97cdc5d1a89477c436b61f472c4d40971774ac4729c613b4b133163cb"},
- {file = "multidict-6.6.4-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:0b2e886624be5773e69cf32bcb8534aecdeb38943520b240fed3d5596a430f2f"},
- {file = "multidict-6.6.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:be5bf4b3224948032a845d12ab0f69f208293742df96dc14c4ff9b09e508fc17"},
- {file = "multidict-6.6.4-cp39-cp39-win32.whl", hash = "sha256:10a68a9191f284fe9d501fef4efe93226e74df92ce7a24e301371293bd4918ae"},
- {file = "multidict-6.6.4-cp39-cp39-win_amd64.whl", hash = "sha256:ee25f82f53262f9ac93bd7e58e47ea1bdcc3393cef815847e397cba17e284210"},
- {file = "multidict-6.6.4-cp39-cp39-win_arm64.whl", hash = "sha256:f9867e55590e0855bcec60d4f9a092b69476db64573c9fe17e92b0c50614c16a"},
- {file = "multidict-6.6.4-py3-none-any.whl", hash = "sha256:27d8f8e125c07cb954e54d75d04905a9bba8a439c1d84aca94949d4d03d8601c"},
- {file = "multidict-6.6.4.tar.gz", hash = "sha256:d2d4e4787672911b48350df02ed3fa3fffdc2f2e8ca06dd6afdf34189b76a9dd"},
-]
-
-[package.dependencies]
-typing-extensions = {version = ">=4.1.0", markers = "python_version < \"3.11\""}
-
-[[package]]
-name = "multiprocess"
-version = "0.70.16"
-description = "better multiprocessing and multithreading in Python"
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "multiprocess-0.70.16-pp310-pypy310_pp73-macosx_10_13_x86_64.whl", hash = "sha256:476887be10e2f59ff183c006af746cb6f1fd0eadcfd4ef49e605cbe2659920ee"},
- {file = "multiprocess-0.70.16-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d951bed82c8f73929ac82c61f01a7b5ce8f3e5ef40f5b52553b4f547ce2b08ec"},
- {file = "multiprocess-0.70.16-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:37b55f71c07e2d741374998c043b9520b626a8dddc8b3129222ca4f1a06ef67a"},
- {file = "multiprocess-0.70.16-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba8c31889abf4511c7308a8c52bb4a30b9d590e7f58523302ba00237702ca054"},
- {file = "multiprocess-0.70.16-pp39-pypy39_pp73-macosx_10_13_x86_64.whl", hash = "sha256:0dfd078c306e08d46d7a8d06fb120313d87aa43af60d66da43ffff40b44d2f41"},
- {file = "multiprocess-0.70.16-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e7b9d0f307cd9bd50851afaac0dba2cb6c44449efff697df7c7645f7d3f2be3a"},
- {file = "multiprocess-0.70.16-py310-none-any.whl", hash = "sha256:c4a9944c67bd49f823687463660a2d6daae94c289adff97e0f9d696ba6371d02"},
- {file = "multiprocess-0.70.16-py311-none-any.whl", hash = "sha256:af4cabb0dac72abfb1e794fa7855c325fd2b55a10a44628a3c1ad3311c04127a"},
- {file = "multiprocess-0.70.16-py312-none-any.whl", hash = "sha256:fc0544c531920dde3b00c29863377f87e1632601092ea2daca74e4beb40faa2e"},
- {file = "multiprocess-0.70.16-py38-none-any.whl", hash = "sha256:a71d82033454891091a226dfc319d0cfa8019a4e888ef9ca910372a446de4435"},
- {file = "multiprocess-0.70.16-py39-none-any.whl", hash = "sha256:a0bafd3ae1b732eac64be2e72038231c1ba97724b60b09400d68f229fcc2fbf3"},
- {file = "multiprocess-0.70.16.tar.gz", hash = "sha256:161af703d4652a0e1410be6abccecde4a7ddffd19341be0a7011b94aeb171ac1"},
-]
-
-[package.dependencies]
-dill = ">=0.3.8"
-
-[[package]]
-name = "narwhals"
-version = "2.3.0"
-description = "Extremely lightweight compatibility layer between dataframe libraries"
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "narwhals-2.3.0-py3-none-any.whl", hash = "sha256:5507b1a9a9c2b1c55a627fdf6cf722fef2e23498bd14362a332c8848a311c321"},
- {file = "narwhals-2.3.0.tar.gz", hash = "sha256:b66bc4ab7b6746354f60c4b3941e3ce60c066588c35360e2dc6c063489000a16"},
-]
-
-[package.extras]
-cudf = ["cudf (>=24.10.0)"]
-dask = ["dask[dataframe] (>=2024.8)"]
-duckdb = ["duckdb (>=1.0)"]
-ibis = ["ibis-framework (>=6.0.0)", "packaging", "pyarrow-hotfix", "rich"]
-modin = ["modin"]
-pandas = ["pandas (>=1.1.3)"]
-polars = ["polars (>=0.20.4)"]
-pyarrow = ["pyarrow (>=13.0.0)"]
-pyspark = ["pyspark (>=3.5.0)"]
-pyspark-connect = ["pyspark[connect] (>=3.5.0)"]
-sqlframe = ["sqlframe (>=3.22.0,!=3.39.3)"]
-
-[[package]]
-name = "nbclient"
-version = "0.10.2"
-description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor."
-optional = false
-python-versions = ">=3.9.0"
-groups = ["dev"]
-files = [
- {file = "nbclient-0.10.2-py3-none-any.whl", hash = "sha256:4ffee11e788b4a27fabeb7955547e4318a5298f34342a4bfd01f2e1faaeadc3d"},
- {file = "nbclient-0.10.2.tar.gz", hash = "sha256:90b7fc6b810630db87a6d0c2250b1f0ab4cf4d3c27a299b0cde78a4ed3fd9193"},
-]
-
-[package.dependencies]
-jupyter-client = ">=6.1.12"
-jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0"
-nbformat = ">=5.1"
-traitlets = ">=5.4"
-
-[package.extras]
-dev = ["pre-commit"]
-docs = ["autodoc-traits", "flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "mock", "moto", "myst-parser", "nbconvert (>=7.1.0)", "pytest (>=7.0,<8)", "pytest-asyncio", "pytest-cov (>=4.0)", "sphinx (>=1.7)", "sphinx-book-theme", "sphinxcontrib-spelling", "testpath", "xmltodict"]
-test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>=7.1.0)", "pytest (>=7.0,<8)", "pytest-asyncio", "pytest-cov (>=4.0)", "testpath", "xmltodict"]
-
-[[package]]
-name = "nbconvert"
-version = "7.16.6"
-description = "Converting Jupyter Notebooks (.ipynb files) to other formats. Output formats include asciidoc, html, latex, markdown, pdf, py, rst, script. nbconvert can be used both as a Python library (`import nbconvert`) or as a command line tool (invoked as `jupyter nbconvert ...`)."
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "nbconvert-7.16.6-py3-none-any.whl", hash = "sha256:1375a7b67e0c2883678c48e506dc320febb57685e5ee67faa51b18a90f3a712b"},
- {file = "nbconvert-7.16.6.tar.gz", hash = "sha256:576a7e37c6480da7b8465eefa66c17844243816ce1ccc372633c6b71c3c0f582"},
-]
-
-[package.dependencies]
-beautifulsoup4 = "*"
-bleach = {version = "!=5.0.0", extras = ["css"]}
-defusedxml = "*"
-jinja2 = ">=3.0"
-jupyter-core = ">=4.7"
-jupyterlab-pygments = "*"
-markupsafe = ">=2.0"
-mistune = ">=2.0.3,<4"
-nbclient = ">=0.5.0"
-nbformat = ">=5.7"
-packaging = "*"
-pandocfilters = ">=1.4.1"
-pygments = ">=2.4.1"
-traitlets = ">=5.1"
-
-[package.extras]
-all = ["flaky", "ipykernel", "ipython", "ipywidgets (>=7.5)", "myst-parser", "nbsphinx (>=0.2.12)", "playwright", "pydata-sphinx-theme", "pyqtwebengine (>=5.15)", "pytest (>=7)", "sphinx (==5.0.2)", "sphinxcontrib-spelling", "tornado (>=6.1)"]
-docs = ["ipykernel", "ipython", "myst-parser", "nbsphinx (>=0.2.12)", "pydata-sphinx-theme", "sphinx (==5.0.2)", "sphinxcontrib-spelling"]
-qtpdf = ["pyqtwebengine (>=5.15)"]
-qtpng = ["pyqtwebengine (>=5.15)"]
-serve = ["tornado (>=6.1)"]
-test = ["flaky", "ipykernel", "ipywidgets (>=7.5)", "pytest (>=7)"]
-webpdf = ["playwright"]
-
-[[package]]
-name = "nbformat"
-version = "5.10.4"
-description = "The Jupyter Notebook format"
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b"},
- {file = "nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a"},
-]
-
-[package.dependencies]
-fastjsonschema = ">=2.15"
-jsonschema = ">=2.6"
-jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0"
-traitlets = ">=5.1"
-
-[package.extras]
-docs = ["myst-parser", "pydata-sphinx-theme", "sphinx", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"]
-test = ["pep440", "pre-commit", "pytest", "testpath"]
-
-[[package]]
-name = "nest-asyncio"
-version = "1.6.0"
-description = "Patch asyncio to allow nested event loops"
-optional = false
-python-versions = ">=3.5"
-groups = ["dev"]
-files = [
- {file = "nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c"},
- {file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"},
-]
-
-[[package]]
-name = "networkx"
-version = "3.4.2"
-description = "Python package for creating and manipulating graphs and networks"
-optional = false
-python-versions = ">=3.10"
-groups = ["main"]
-markers = "python_version == \"3.10\""
-files = [
- {file = "networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f"},
- {file = "networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1"},
-]
-
-[package.extras]
-default = ["matplotlib (>=3.7)", "numpy (>=1.24)", "pandas (>=2.0)", "scipy (>=1.10,!=1.11.0,!=1.11.1)"]
-developer = ["changelist (==0.5)", "mypy (>=1.1)", "pre-commit (>=3.2)", "rtoml"]
-doc = ["intersphinx-registry", "myst-nb (>=1.1)", "numpydoc (>=1.8.0)", "pillow (>=9.4)", "pydata-sphinx-theme (>=0.15)", "sphinx (>=7.3)", "sphinx-gallery (>=0.16)", "texext (>=0.6.7)"]
-example = ["cairocffi (>=1.7)", "contextily (>=1.6)", "igraph (>=0.11)", "momepy (>=0.7.2)", "osmnx (>=1.9)", "scikit-learn (>=1.5)", "seaborn (>=0.13)"]
-extra = ["lxml (>=4.6)", "pydot (>=3.0.1)", "pygraphviz (>=1.14)", "sympy (>=1.10)"]
-test = ["pytest (>=7.2)", "pytest-cov (>=4.0)"]
-
-[[package]]
-name = "networkx"
-version = "3.5"
-description = "Python package for creating and manipulating graphs and networks"
-optional = false
-python-versions = ">=3.11"
-groups = ["main"]
-markers = "python_version >= \"3.11\""
-files = [
- {file = "networkx-3.5-py3-none-any.whl", hash = "sha256:0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec"},
- {file = "networkx-3.5.tar.gz", hash = "sha256:d4c6f9cf81f52d69230866796b82afbccdec3db7ae4fbd1b65ea750feed50037"},
-]
-
-[package.extras]
-default = ["matplotlib (>=3.8)", "numpy (>=1.25)", "pandas (>=2.0)", "scipy (>=1.11.2)"]
-developer = ["mypy (>=1.15)", "pre-commit (>=4.1)"]
-doc = ["intersphinx-registry", "myst-nb (>=1.1)", "numpydoc (>=1.8.0)", "pillow (>=10)", "pydata-sphinx-theme (>=0.16)", "sphinx (>=8.0)", "sphinx-gallery (>=0.18)", "texext (>=0.6.7)"]
-example = ["cairocffi (>=1.7)", "contextily (>=1.6)", "igraph (>=0.11)", "momepy (>=0.7.2)", "osmnx (>=2.0.0)", "scikit-learn (>=1.5)", "seaborn (>=0.13)"]
-extra = ["lxml (>=4.6)", "pydot (>=3.0.1)", "pygraphviz (>=1.14)", "sympy (>=1.10)"]
-test = ["pytest (>=7.2)", "pytest-cov (>=4.0)", "pytest-xdist (>=3.0)"]
-test-extras = ["pytest-mpl", "pytest-randomly"]
-
-[[package]]
-name = "nodeenv"
-version = "1.9.1"
-description = "Node.js virtual environment builder"
-optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
-groups = ["main"]
-files = [
- {file = "nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9"},
- {file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"},
-]
-
-[[package]]
-name = "notebook"
-version = "7.4.5"
-description = "Jupyter Notebook - A web-based notebook environment for interactive computing"
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "notebook-7.4.5-py3-none-any.whl", hash = "sha256:351635461aca9dad08cf8946a4216f963e2760cc1bf7b1aaaecb23afc33ec046"},
- {file = "notebook-7.4.5.tar.gz", hash = "sha256:7c2c4ea245913c3ad8ab3e5d36b34a842c06e524556f5c2e1f5d7d08c986615e"},
-]
-
-[package.dependencies]
-jupyter-server = ">=2.4.0,<3"
-jupyterlab = ">=4.4.5,<4.5"
-jupyterlab-server = ">=2.27.1,<3"
-notebook-shim = ">=0.2,<0.3"
-tornado = ">=6.2.0"
-
-[package.extras]
-dev = ["hatch", "pre-commit"]
-docs = ["myst-parser", "nbsphinx", "pydata-sphinx-theme", "sphinx (>=1.3.6)", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"]
-test = ["importlib-resources (>=5.0) ; python_version < \"3.10\"", "ipykernel", "jupyter-server[test] (>=2.4.0,<3)", "jupyterlab-server[test] (>=2.27.1,<3)", "nbval", "pytest (>=7.0)", "pytest-console-scripts", "pytest-timeout", "pytest-tornasync", "requests"]
-
-[[package]]
-name = "notebook-shim"
-version = "0.2.4"
-description = "A shim layer for notebook traits and config"
-optional = false
-python-versions = ">=3.7"
-groups = ["dev"]
-files = [
- {file = "notebook_shim-0.2.4-py3-none-any.whl", hash = "sha256:411a5be4e9dc882a074ccbcae671eda64cceb068767e9a3419096986560e1cef"},
- {file = "notebook_shim-0.2.4.tar.gz", hash = "sha256:b4b2cfa1b65d98307ca24361f5b30fe785b53c3fd07b7a47e89acb5e6ac638cb"},
-]
-
-[package.dependencies]
-jupyter-server = ">=1.8,<3"
-
-[package.extras]
-test = ["pytest", "pytest-console-scripts", "pytest-jupyter", "pytest-tornasync"]
-
-[[package]]
-name = "numpy"
-version = "2.2.6"
-description = "Fundamental package for array computing in Python"
-optional = false
-python-versions = ">=3.10"
-groups = ["main", "dev", "xla"]
-markers = "python_version == \"3.10\""
-files = [
- {file = "numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb"},
- {file = "numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90"},
- {file = "numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163"},
- {file = "numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf"},
- {file = "numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83"},
- {file = "numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915"},
- {file = "numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680"},
- {file = "numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289"},
- {file = "numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d"},
- {file = "numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3"},
- {file = "numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae"},
- {file = "numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a"},
- {file = "numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42"},
- {file = "numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491"},
- {file = "numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a"},
- {file = "numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf"},
- {file = "numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1"},
- {file = "numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab"},
- {file = "numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47"},
- {file = "numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303"},
- {file = "numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff"},
- {file = "numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c"},
- {file = "numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3"},
- {file = "numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282"},
- {file = "numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87"},
- {file = "numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249"},
- {file = "numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49"},
- {file = "numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de"},
- {file = "numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4"},
- {file = "numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2"},
- {file = "numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84"},
- {file = "numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b"},
- {file = "numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d"},
- {file = "numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566"},
- {file = "numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f"},
- {file = "numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f"},
- {file = "numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868"},
- {file = "numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d"},
- {file = "numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd"},
- {file = "numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c"},
- {file = "numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6"},
- {file = "numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda"},
- {file = "numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40"},
- {file = "numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8"},
- {file = "numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f"},
- {file = "numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa"},
- {file = "numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571"},
- {file = "numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1"},
- {file = "numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff"},
- {file = "numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06"},
- {file = "numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d"},
- {file = "numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db"},
- {file = "numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543"},
- {file = "numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00"},
- {file = "numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd"},
-]
-
-[[package]]
-name = "numpy"
-version = "2.3.2"
-description = "Fundamental package for array computing in Python"
-optional = false
-python-versions = ">=3.11"
-groups = ["main", "dev", "xla"]
-markers = "python_version >= \"3.11\""
-files = [
- {file = "numpy-2.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:852ae5bed3478b92f093e30f785c98e0cb62fa0a939ed057c31716e18a7a22b9"},
- {file = "numpy-2.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a0e27186e781a69959d0230dd9909b5e26024f8da10683bd6344baea1885168"},
- {file = "numpy-2.3.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:f0a1a8476ad77a228e41619af2fa9505cf69df928e9aaa165746584ea17fed2b"},
- {file = "numpy-2.3.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:cbc95b3813920145032412f7e33d12080f11dc776262df1712e1638207dde9e8"},
- {file = "numpy-2.3.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f75018be4980a7324edc5930fe39aa391d5734531b1926968605416ff58c332d"},
- {file = "numpy-2.3.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20b8200721840f5621b7bd03f8dcd78de33ec522fc40dc2641aa09537df010c3"},
- {file = "numpy-2.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1f91e5c028504660d606340a084db4b216567ded1056ea2b4be4f9d10b67197f"},
- {file = "numpy-2.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:fb1752a3bb9a3ad2d6b090b88a9a0ae1cd6f004ef95f75825e2f382c183b2097"},
- {file = "numpy-2.3.2-cp311-cp311-win32.whl", hash = "sha256:4ae6863868aaee2f57503c7a5052b3a2807cf7a3914475e637a0ecd366ced220"},
- {file = "numpy-2.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:240259d6564f1c65424bcd10f435145a7644a65a6811cfc3201c4a429ba79170"},
- {file = "numpy-2.3.2-cp311-cp311-win_arm64.whl", hash = "sha256:4209f874d45f921bde2cff1ffcd8a3695f545ad2ffbef6d3d3c6768162efab89"},
- {file = "numpy-2.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bc3186bea41fae9d8e90c2b4fb5f0a1f5a690682da79b92574d63f56b529080b"},
- {file = "numpy-2.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2f4f0215edb189048a3c03bd5b19345bdfa7b45a7a6f72ae5945d2a28272727f"},
- {file = "numpy-2.3.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8b1224a734cd509f70816455c3cffe13a4f599b1bf7130f913ba0e2c0b2006c0"},
- {file = "numpy-2.3.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:3dcf02866b977a38ba3ec10215220609ab9667378a9e2150615673f3ffd6c73b"},
- {file = "numpy-2.3.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:572d5512df5470f50ada8d1972c5f1082d9a0b7aa5944db8084077570cf98370"},
- {file = "numpy-2.3.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8145dd6d10df13c559d1e4314df29695613575183fa2e2d11fac4c208c8a1f73"},
- {file = "numpy-2.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:103ea7063fa624af04a791c39f97070bf93b96d7af7eb23530cd087dc8dbe9dc"},
- {file = "numpy-2.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc927d7f289d14f5e037be917539620603294454130b6de200091e23d27dc9be"},
- {file = "numpy-2.3.2-cp312-cp312-win32.whl", hash = "sha256:d95f59afe7f808c103be692175008bab926b59309ade3e6d25009e9a171f7036"},
- {file = "numpy-2.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:9e196ade2400c0c737d93465327d1ae7c06c7cb8a1756121ebf54b06ca183c7f"},
- {file = "numpy-2.3.2-cp312-cp312-win_arm64.whl", hash = "sha256:ee807923782faaf60d0d7331f5e86da7d5e3079e28b291973c545476c2b00d07"},
- {file = "numpy-2.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c8d9727f5316a256425892b043736d63e89ed15bbfe6556c5ff4d9d4448ff3b3"},
- {file = "numpy-2.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:efc81393f25f14d11c9d161e46e6ee348637c0a1e8a54bf9dedc472a3fae993b"},
- {file = "numpy-2.3.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:dd937f088a2df683cbb79dda9a772b62a3e5a8a7e76690612c2737f38c6ef1b6"},
- {file = "numpy-2.3.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:11e58218c0c46c80509186e460d79fbdc9ca1eb8d8aee39d8f2dc768eb781089"},
- {file = "numpy-2.3.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5ad4ebcb683a1f99f4f392cc522ee20a18b2bb12a2c1c42c3d48d5a1adc9d3d2"},
- {file = "numpy-2.3.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:938065908d1d869c7d75d8ec45f735a034771c6ea07088867f713d1cd3bbbe4f"},
- {file = "numpy-2.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:66459dccc65d8ec98cc7df61307b64bf9e08101f9598755d42d8ae65d9a7a6ee"},
- {file = "numpy-2.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a7af9ed2aa9ec5950daf05bb11abc4076a108bd3c7db9aa7251d5f107079b6a6"},
- {file = "numpy-2.3.2-cp313-cp313-win32.whl", hash = "sha256:906a30249315f9c8e17b085cc5f87d3f369b35fedd0051d4a84686967bdbbd0b"},
- {file = "numpy-2.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:c63d95dc9d67b676e9108fe0d2182987ccb0f11933c1e8959f42fa0da8d4fa56"},
- {file = "numpy-2.3.2-cp313-cp313-win_arm64.whl", hash = "sha256:b05a89f2fb84d21235f93de47129dd4f11c16f64c87c33f5e284e6a3a54e43f2"},
- {file = "numpy-2.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4e6ecfeddfa83b02318f4d84acf15fbdbf9ded18e46989a15a8b6995dfbf85ab"},
- {file = "numpy-2.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:508b0eada3eded10a3b55725b40806a4b855961040180028f52580c4729916a2"},
- {file = "numpy-2.3.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:754d6755d9a7588bdc6ac47dc4ee97867271b17cee39cb87aef079574366db0a"},
- {file = "numpy-2.3.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:a9f66e7d2b2d7712410d3bc5684149040ef5f19856f20277cd17ea83e5006286"},
- {file = "numpy-2.3.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:de6ea4e5a65d5a90c7d286ddff2b87f3f4ad61faa3db8dabe936b34c2275b6f8"},
- {file = "numpy-2.3.2-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3ef07ec8cbc8fc9e369c8dcd52019510c12da4de81367d8b20bc692aa07573a"},
- {file = "numpy-2.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:27c9f90e7481275c7800dc9c24b7cc40ace3fdb970ae4d21eaff983a32f70c91"},
- {file = "numpy-2.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:07b62978075b67eee4065b166d000d457c82a1efe726cce608b9db9dd66a73a5"},
- {file = "numpy-2.3.2-cp313-cp313t-win32.whl", hash = "sha256:c771cfac34a4f2c0de8e8c97312d07d64fd8f8ed45bc9f5726a7e947270152b5"},
- {file = "numpy-2.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:72dbebb2dcc8305c431b2836bcc66af967df91be793d63a24e3d9b741374c450"},
- {file = "numpy-2.3.2-cp313-cp313t-win_arm64.whl", hash = "sha256:72c6df2267e926a6d5286b0a6d556ebe49eae261062059317837fda12ddf0c1a"},
- {file = "numpy-2.3.2-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:448a66d052d0cf14ce9865d159bfc403282c9bc7bb2a31b03cc18b651eca8b1a"},
- {file = "numpy-2.3.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:546aaf78e81b4081b2eba1d105c3b34064783027a06b3ab20b6eba21fb64132b"},
- {file = "numpy-2.3.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:87c930d52f45df092f7578889711a0768094debf73cfcde105e2d66954358125"},
- {file = "numpy-2.3.2-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:8dc082ea901a62edb8f59713c6a7e28a85daddcb67454c839de57656478f5b19"},
- {file = "numpy-2.3.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:af58de8745f7fa9ca1c0c7c943616c6fe28e75d0c81f5c295810e3c83b5be92f"},
- {file = "numpy-2.3.2-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fed5527c4cf10f16c6d0b6bee1f89958bccb0ad2522c8cadc2efd318bcd545f5"},
- {file = "numpy-2.3.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:095737ed986e00393ec18ec0b21b47c22889ae4b0cd2d5e88342e08b01141f58"},
- {file = "numpy-2.3.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b5e40e80299607f597e1a8a247ff8d71d79c5b52baa11cc1cce30aa92d2da6e0"},
- {file = "numpy-2.3.2-cp314-cp314-win32.whl", hash = "sha256:7d6e390423cc1f76e1b8108c9b6889d20a7a1f59d9a60cac4a050fa734d6c1e2"},
- {file = "numpy-2.3.2-cp314-cp314-win_amd64.whl", hash = "sha256:b9d0878b21e3918d76d2209c924ebb272340da1fb51abc00f986c258cd5e957b"},
- {file = "numpy-2.3.2-cp314-cp314-win_arm64.whl", hash = "sha256:2738534837c6a1d0c39340a190177d7d66fdf432894f469728da901f8f6dc910"},
- {file = "numpy-2.3.2-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:4d002ecf7c9b53240be3bb69d80f86ddbd34078bae04d87be81c1f58466f264e"},
- {file = "numpy-2.3.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:293b2192c6bcce487dbc6326de5853787f870aeb6c43f8f9c6496db5b1781e45"},
- {file = "numpy-2.3.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:0a4f2021a6da53a0d580d6ef5db29947025ae8b35b3250141805ea9a32bbe86b"},
- {file = "numpy-2.3.2-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:9c144440db4bf3bb6372d2c3e49834cc0ff7bb4c24975ab33e01199e645416f2"},
- {file = "numpy-2.3.2-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f92d6c2a8535dc4fe4419562294ff957f83a16ebdec66df0805e473ffaad8bd0"},
- {file = "numpy-2.3.2-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cefc2219baa48e468e3db7e706305fcd0c095534a192a08f31e98d83a7d45fb0"},
- {file = "numpy-2.3.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:76c3e9501ceb50b2ff3824c3589d5d1ab4ac857b0ee3f8f49629d0de55ecf7c2"},
- {file = "numpy-2.3.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:122bf5ed9a0221b3419672493878ba4967121514b1d7d4656a7580cd11dddcbf"},
- {file = "numpy-2.3.2-cp314-cp314t-win32.whl", hash = "sha256:6f1ae3dcb840edccc45af496f312528c15b1f79ac318169d094e85e4bb35fdf1"},
- {file = "numpy-2.3.2-cp314-cp314t-win_amd64.whl", hash = "sha256:087ffc25890d89a43536f75c5fe8770922008758e8eeeef61733957041ed2f9b"},
- {file = "numpy-2.3.2-cp314-cp314t-win_arm64.whl", hash = "sha256:092aeb3449833ea9c0bf0089d70c29ae480685dd2377ec9cdbbb620257f84631"},
- {file = "numpy-2.3.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:14a91ebac98813a49bc6aa1a0dfc09513dcec1d97eaf31ca21a87221a1cdcb15"},
- {file = "numpy-2.3.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:71669b5daae692189540cffc4c439468d35a3f84f0c88b078ecd94337f6cb0ec"},
- {file = "numpy-2.3.2-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:69779198d9caee6e547adb933941ed7520f896fd9656834c300bdf4dd8642712"},
- {file = "numpy-2.3.2-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:2c3271cc4097beb5a60f010bcc1cc204b300bb3eafb4399376418a83a1c6373c"},
- {file = "numpy-2.3.2-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8446acd11fe3dc1830568c941d44449fd5cb83068e5c70bd5a470d323d448296"},
- {file = "numpy-2.3.2-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aa098a5ab53fa407fded5870865c6275a5cd4101cfdef8d6fafc48286a96e981"},
- {file = "numpy-2.3.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6936aff90dda378c09bea075af0d9c675fe3a977a9d2402f95a87f440f59f619"},
- {file = "numpy-2.3.2.tar.gz", hash = "sha256:e0486a11ec30cdecb53f184d496d1c6a20786c81e55e41640270130056f8ee48"},
-]
-
-[[package]]
-name = "nvidia-cublas-cu12"
-version = "12.6.4.1"
-description = "CUBLAS native runtime libraries"
-optional = false
-python-versions = ">=3"
-groups = ["main"]
-markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.14\""
-files = [
- {file = "nvidia_cublas_cu12-12.6.4.1-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:08ed2686e9875d01b58e3cb379c6896df8e76c75e0d4a7f7dace3d7b6d9ef8eb"},
- {file = "nvidia_cublas_cu12-12.6.4.1-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:235f728d6e2a409eddf1df58d5b0921cf80cfa9e72b9f2775ccb7b4a87984668"},
- {file = "nvidia_cublas_cu12-12.6.4.1-py3-none-win_amd64.whl", hash = "sha256:9e4fa264f4d8a4eb0cdbd34beadc029f453b3bafae02401e999cf3d5a5af75f8"},
-]
-
-[[package]]
-name = "nvidia-cublas-cu12"
-version = "12.8.4.1"
-description = "CUBLAS native runtime libraries"
-optional = false
-python-versions = ">=3"
-groups = ["main"]
-markers = "python_version < \"3.14\" and platform_machine == \"x86_64\" and platform_system == \"Linux\""
-files = [
- {file = "nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:b86f6dd8935884615a0683b663891d43781b819ac4f2ba2b0c9604676af346d0"},
- {file = "nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142"},
- {file = "nvidia_cublas_cu12-12.8.4.1-py3-none-win_amd64.whl", hash = "sha256:47e9b82132fa8d2b4944e708049229601448aaad7e6f296f630f2d1a32de35af"},
-]
-
-[[package]]
-name = "nvidia-cuda-cupti-cu12"
-version = "12.6.80"
-description = "CUDA profiling tools runtime libs."
-optional = false
-python-versions = ">=3"
-groups = ["main"]
-markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.14\""
-files = [
- {file = "nvidia_cuda_cupti_cu12-12.6.80-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:166ee35a3ff1587f2490364f90eeeb8da06cd867bd5b701bf7f9a02b78bc63fc"},
- {file = "nvidia_cuda_cupti_cu12-12.6.80-py3-none-manylinux2014_aarch64.whl", hash = "sha256:358b4a1d35370353d52e12f0a7d1769fc01ff74a191689d3870b2123156184c4"},
- {file = "nvidia_cuda_cupti_cu12-12.6.80-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6768bad6cab4f19e8292125e5f1ac8aa7d1718704012a0e3272a6f61c4bce132"},
- {file = "nvidia_cuda_cupti_cu12-12.6.80-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a3eff6cdfcc6a4c35db968a06fcadb061cbc7d6dde548609a941ff8701b98b73"},
- {file = "nvidia_cuda_cupti_cu12-12.6.80-py3-none-win_amd64.whl", hash = "sha256:bbe6ae76e83ce5251b56e8c8e61a964f757175682bbad058b170b136266ab00a"},
-]
-
-[[package]]
-name = "nvidia-cuda-cupti-cu12"
-version = "12.8.90"
-description = "CUDA profiling tools runtime libs."
-optional = false
-python-versions = ">=3"
-groups = ["main"]
-markers = "python_version < \"3.14\" and platform_machine == \"x86_64\" and platform_system == \"Linux\""
-files = [
- {file = "nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4412396548808ddfed3f17a467b104ba7751e6b58678a4b840675c56d21cf7ed"},
- {file = "nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182"},
- {file = "nvidia_cuda_cupti_cu12-12.8.90-py3-none-win_amd64.whl", hash = "sha256:bb479dcdf7e6d4f8b0b01b115260399bf34154a1a2e9fe11c85c517d87efd98e"},
-]
-
-[[package]]
-name = "nvidia-cuda-nvrtc-cu12"
-version = "12.6.77"
-description = "NVRTC native runtime libraries"
-optional = false
-python-versions = ">=3"
-groups = ["main"]
-markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.14\""
-files = [
- {file = "nvidia_cuda_nvrtc_cu12-12.6.77-py3-none-manylinux2014_aarch64.whl", hash = "sha256:5847f1d6e5b757f1d2b3991a01082a44aad6f10ab3c5c0213fa3e25bddc25a13"},
- {file = "nvidia_cuda_nvrtc_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:35b0cc6ee3a9636d5409133e79273ce1f3fd087abb0532d2d2e8fff1fe9efc53"},
- {file = "nvidia_cuda_nvrtc_cu12-12.6.77-py3-none-win_amd64.whl", hash = "sha256:f7007dbd914c56bd80ea31bc43e8e149da38f68158f423ba845fc3292684e45a"},
-]
-
-[[package]]
-name = "nvidia-cuda-nvrtc-cu12"
-version = "12.8.93"
-description = "NVRTC native runtime libraries"
-optional = false
-python-versions = ">=3"
-groups = ["main"]
-markers = "python_version < \"3.14\" and platform_machine == \"x86_64\" and platform_system == \"Linux\""
-files = [
- {file = "nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994"},
- {file = "nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fc1fec1e1637854b4c0a65fb9a8346b51dd9ee69e61ebaccc82058441f15bce8"},
- {file = "nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-win_amd64.whl", hash = "sha256:7a4b6b2904850fe78e0bd179c4b655c404d4bb799ef03ddc60804247099ae909"},
-]
-
-[[package]]
-name = "nvidia-cuda-runtime-cu12"
-version = "12.6.77"
-description = "CUDA Runtime native Libraries"
-optional = false
-python-versions = ">=3"
-groups = ["main"]
-markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.14\""
-files = [
- {file = "nvidia_cuda_runtime_cu12-12.6.77-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6116fad3e049e04791c0256a9778c16237837c08b27ed8c8401e2e45de8d60cd"},
- {file = "nvidia_cuda_runtime_cu12-12.6.77-py3-none-manylinux2014_aarch64.whl", hash = "sha256:d461264ecb429c84c8879a7153499ddc7b19b5f8d84c204307491989a365588e"},
- {file = "nvidia_cuda_runtime_cu12-12.6.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ba3b56a4f896141e25e19ab287cd71e52a6a0f4b29d0d31609f60e3b4d5219b7"},
- {file = "nvidia_cuda_runtime_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:a84d15d5e1da416dd4774cb42edf5e954a3e60cc945698dc1d5be02321c44dc8"},
- {file = "nvidia_cuda_runtime_cu12-12.6.77-py3-none-win_amd64.whl", hash = "sha256:86c58044c824bf3c173c49a2dbc7a6c8b53cb4e4dca50068be0bf64e9dab3f7f"},
-]
-
-[[package]]
-name = "nvidia-cuda-runtime-cu12"
-version = "12.8.90"
-description = "CUDA Runtime native Libraries"
-optional = false
-python-versions = ">=3"
-groups = ["main"]
-markers = "python_version < \"3.14\" and platform_machine == \"x86_64\" and platform_system == \"Linux\""
-files = [
- {file = "nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:52bf7bbee900262ffefe5e9d5a2a69a30d97e2bc5bb6cc866688caa976966e3d"},
- {file = "nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90"},
- {file = "nvidia_cuda_runtime_cu12-12.8.90-py3-none-win_amd64.whl", hash = "sha256:c0c6027f01505bfed6c3b21ec546f69c687689aad5f1a377554bc6ca4aa993a8"},
-]
-
-[[package]]
-name = "nvidia-cudnn-cu12"
-version = "9.5.1.17"
-description = "cuDNN runtime libraries"
-optional = false
-python-versions = ">=3"
-groups = ["main"]
-markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.14\""
-files = [
- {file = "nvidia_cudnn_cu12-9.5.1.17-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:9fd4584468533c61873e5fda8ca41bac3a38bcb2d12350830c69b0a96a7e4def"},
- {file = "nvidia_cudnn_cu12-9.5.1.17-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:30ac3869f6db17d170e0e556dd6cc5eee02647abc31ca856634d5a40f82c15b2"},
- {file = "nvidia_cudnn_cu12-9.5.1.17-py3-none-win_amd64.whl", hash = "sha256:d7af0f8a4f3b4b9dbb3122f2ef553b45694ed9c384d5a75bab197b8eefb79ab8"},
-]
-
-[package.dependencies]
-nvidia-cublas-cu12 = "*"
-
-[[package]]
-name = "nvidia-cudnn-cu12"
-version = "9.10.2.21"
-description = "cuDNN runtime libraries"
-optional = false
-python-versions = ">=3"
-groups = ["main"]
-markers = "python_version < \"3.14\" and platform_machine == \"x86_64\" and platform_system == \"Linux\""
-files = [
- {file = "nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:c9132cc3f8958447b4910a1720036d9eff5928cc3179b0a51fb6d167c6cc87d8"},
- {file = "nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8"},
- {file = "nvidia_cudnn_cu12-9.10.2.21-py3-none-win_amd64.whl", hash = "sha256:c6288de7d63e6cf62988f0923f96dc339cea362decb1bf5b3141883392a7d65e"},
-]
-
-[package.dependencies]
-nvidia-cublas-cu12 = "*"
-
-[[package]]
-name = "nvidia-cufft-cu12"
-version = "11.3.0.4"
-description = "CUFFT native runtime libraries"
-optional = false
-python-versions = ">=3"
-groups = ["main"]
-markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.14\""
-files = [
- {file = "nvidia_cufft_cu12-11.3.0.4-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d16079550df460376455cba121db6564089176d9bac9e4f360493ca4741b22a6"},
- {file = "nvidia_cufft_cu12-11.3.0.4-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8510990de9f96c803a051822618d42bf6cb8f069ff3f48d93a8486efdacb48fb"},
- {file = "nvidia_cufft_cu12-11.3.0.4-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ccba62eb9cef5559abd5e0d54ceed2d9934030f51163df018532142a8ec533e5"},
- {file = "nvidia_cufft_cu12-11.3.0.4-py3-none-manylinux2014_x86_64.whl", hash = "sha256:768160ac89f6f7b459bee747e8d175dbf53619cfe74b2a5636264163138013ca"},
- {file = "nvidia_cufft_cu12-11.3.0.4-py3-none-win_amd64.whl", hash = "sha256:6048ebddfb90d09d2707efb1fd78d4e3a77cb3ae4dc60e19aab6be0ece2ae464"},
-]
-
-[package.dependencies]
-nvidia-nvjitlink-cu12 = "*"
-
-[[package]]
-name = "nvidia-cufft-cu12"
-version = "11.3.3.83"
-description = "CUFFT native runtime libraries"
-optional = false
-python-versions = ">=3"
-groups = ["main"]
-markers = "python_version < \"3.14\" and platform_machine == \"x86_64\" and platform_system == \"Linux\""
-files = [
- {file = "nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:848ef7224d6305cdb2a4df928759dca7b1201874787083b6e7550dd6765ce69a"},
- {file = "nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74"},
- {file = "nvidia_cufft_cu12-11.3.3.83-py3-none-win_amd64.whl", hash = "sha256:7a64a98ef2a7c47f905aaf8931b69a3a43f27c55530c698bb2ed7c75c0b42cb7"},
-]
-
-[package.dependencies]
-nvidia-nvjitlink-cu12 = "*"
-
-[[package]]
-name = "nvidia-cufile-cu12"
-version = "1.11.1.6"
-description = "cuFile GPUDirect libraries"
-optional = false
-python-versions = ">=3"
-groups = ["main"]
-markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.14\""
-files = [
- {file = "nvidia_cufile_cu12-1.11.1.6-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc23469d1c7e52ce6c1d55253273d32c565dd22068647f3aa59b3c6b005bf159"},
- {file = "nvidia_cufile_cu12-1.11.1.6-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:8f57a0051dcf2543f6dc2b98a98cb2719c37d3cee1baba8965d57f3bbc90d4db"},
-]
-
-[[package]]
-name = "nvidia-cufile-cu12"
-version = "1.13.1.3"
-description = "cuFile GPUDirect libraries"
-optional = false
-python-versions = ">=3"
-groups = ["main"]
-markers = "python_version < \"3.14\" and platform_machine == \"x86_64\" and platform_system == \"Linux\""
-files = [
- {file = "nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc"},
- {file = "nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:4beb6d4cce47c1a0f1013d72e02b0994730359e17801d395bdcbf20cfb3bb00a"},
-]
-
-[[package]]
-name = "nvidia-curand-cu12"
-version = "10.3.7.77"
-description = "CURAND native runtime libraries"
-optional = false
-python-versions = ">=3"
-groups = ["main"]
-markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.14\""
-files = [
- {file = "nvidia_curand_cu12-10.3.7.77-py3-none-manylinux2014_aarch64.whl", hash = "sha256:6e82df077060ea28e37f48a3ec442a8f47690c7499bff392a5938614b56c98d8"},
- {file = "nvidia_curand_cu12-10.3.7.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a42cd1344297f70b9e39a1e4f467a4e1c10f1da54ff7a85c12197f6c652c8bdf"},
- {file = "nvidia_curand_cu12-10.3.7.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:99f1a32f1ac2bd134897fc7a203f779303261268a65762a623bf30cc9fe79117"},
- {file = "nvidia_curand_cu12-10.3.7.77-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:7b2ed8e95595c3591d984ea3603dd66fe6ce6812b886d59049988a712ed06b6e"},
- {file = "nvidia_curand_cu12-10.3.7.77-py3-none-win_amd64.whl", hash = "sha256:6d6d935ffba0f3d439b7cd968192ff068fafd9018dbf1b85b37261b13cfc9905"},
-]
-
-[[package]]
-name = "nvidia-curand-cu12"
-version = "10.3.9.90"
-description = "CURAND native runtime libraries"
-optional = false
-python-versions = ">=3"
-groups = ["main"]
-markers = "python_version < \"3.14\" and platform_machine == \"x86_64\" and platform_system == \"Linux\""
-files = [
- {file = "nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:dfab99248034673b779bc6decafdc3404a8a6f502462201f2f31f11354204acd"},
- {file = "nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9"},
- {file = "nvidia_curand_cu12-10.3.9.90-py3-none-win_amd64.whl", hash = "sha256:f149a8ca457277da854f89cf282d6ef43176861926c7ac85b2a0fbd237c587ec"},
-]
-
-[[package]]
-name = "nvidia-cusolver-cu12"
-version = "11.7.1.2"
-description = "CUDA solver native runtime libraries"
-optional = false
-python-versions = ">=3"
-groups = ["main"]
-markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.14\""
-files = [
- {file = "nvidia_cusolver_cu12-11.7.1.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:0ce237ef60acde1efc457335a2ddadfd7610b892d94efee7b776c64bb1cac9e0"},
- {file = "nvidia_cusolver_cu12-11.7.1.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e9e49843a7707e42022babb9bcfa33c29857a93b88020c4e4434656a655b698c"},
- {file = "nvidia_cusolver_cu12-11.7.1.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6cf28f17f64107a0c4d7802be5ff5537b2130bfc112f25d5a30df227058ca0e6"},
- {file = "nvidia_cusolver_cu12-11.7.1.2-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:dbbe4fc38ec1289c7e5230e16248365e375c3673c9c8bac5796e2e20db07f56e"},
- {file = "nvidia_cusolver_cu12-11.7.1.2-py3-none-win_amd64.whl", hash = "sha256:6813f9d8073f555444a8705f3ab0296d3e1cb37a16d694c5fc8b862a0d8706d7"},
-]
-
-[package.dependencies]
-nvidia-cublas-cu12 = "*"
-nvidia-cusparse-cu12 = "*"
-nvidia-nvjitlink-cu12 = "*"
-
-[[package]]
-name = "nvidia-cusolver-cu12"
-version = "11.7.3.90"
-description = "CUDA solver native runtime libraries"
-optional = false
-python-versions = ">=3"
-groups = ["main"]
-markers = "python_version < \"3.14\" and platform_machine == \"x86_64\" and platform_system == \"Linux\""
-files = [
- {file = "nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_aarch64.whl", hash = "sha256:db9ed69dbef9715071232caa9b69c52ac7de3a95773c2db65bdba85916e4e5c0"},
- {file = "nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450"},
- {file = "nvidia_cusolver_cu12-11.7.3.90-py3-none-win_amd64.whl", hash = "sha256:4a550db115fcabc4d495eb7d39ac8b58d4ab5d8e63274d3754df1c0ad6a22d34"},
-]
-
-[package.dependencies]
-nvidia-cublas-cu12 = "*"
-nvidia-cusparse-cu12 = "*"
-nvidia-nvjitlink-cu12 = "*"
-
-[[package]]
-name = "nvidia-cusparse-cu12"
-version = "12.5.4.2"
-description = "CUSPARSE native runtime libraries"
-optional = false
-python-versions = ">=3"
-groups = ["main"]
-markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.14\""
-files = [
- {file = "nvidia_cusparse_cu12-12.5.4.2-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d25b62fb18751758fe3c93a4a08eff08effedfe4edf1c6bb5afd0890fe88f887"},
- {file = "nvidia_cusparse_cu12-12.5.4.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7aa32fa5470cf754f72d1116c7cbc300b4e638d3ae5304cfa4a638a5b87161b1"},
- {file = "nvidia_cusparse_cu12-12.5.4.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7556d9eca156e18184b94947ade0fba5bb47d69cec46bf8660fd2c71a4b48b73"},
- {file = "nvidia_cusparse_cu12-12.5.4.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:23749a6571191a215cb74d1cdbff4a86e7b19f1200c071b3fcf844a5bea23a2f"},
- {file = "nvidia_cusparse_cu12-12.5.4.2-py3-none-win_amd64.whl", hash = "sha256:4acb8c08855a26d737398cba8fb6f8f5045d93f82612b4cfd84645a2332ccf20"},
-]
-
-[package.dependencies]
-nvidia-nvjitlink-cu12 = "*"
-
-[[package]]
-name = "nvidia-cusparse-cu12"
-version = "12.5.8.93"
-description = "CUSPARSE native runtime libraries"
-optional = false
-python-versions = ">=3"
-groups = ["main"]
-markers = "python_version < \"3.14\" and platform_machine == \"x86_64\" and platform_system == \"Linux\""
-files = [
- {file = "nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9b6c161cb130be1a07a27ea6923df8141f3c295852f4b260c65f18f3e0a091dc"},
- {file = "nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b"},
- {file = "nvidia_cusparse_cu12-12.5.8.93-py3-none-win_amd64.whl", hash = "sha256:9a33604331cb2cac199f2e7f5104dfbb8a5a898c367a53dfda9ff2acb6b6b4dd"},
-]
-
-[package.dependencies]
-nvidia-nvjitlink-cu12 = "*"
-
-[[package]]
-name = "nvidia-cusparselt-cu12"
-version = "0.6.3"
-description = "NVIDIA cuSPARSELt"
-optional = false
-python-versions = "*"
-groups = ["main"]
-markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.14\""
-files = [
- {file = "nvidia_cusparselt_cu12-0.6.3-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8371549623ba601a06322af2133c4a44350575f5a3108fb75f3ef20b822ad5f1"},
- {file = "nvidia_cusparselt_cu12-0.6.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:e5c8a26c36445dd2e6812f1177978a24e2d37cacce7e090f297a688d1ec44f46"},
- {file = "nvidia_cusparselt_cu12-0.6.3-py3-none-win_amd64.whl", hash = "sha256:3b325bcbd9b754ba43df5a311488fca11a6b5dc3d11df4d190c000cf1a0765c7"},
-]
-
-[[package]]
-name = "nvidia-cusparselt-cu12"
-version = "0.7.1"
-description = "NVIDIA cuSPARSELt"
-optional = false
-python-versions = "*"
-groups = ["main"]
-markers = "python_version < \"3.14\" and platform_machine == \"x86_64\" and platform_system == \"Linux\""
-files = [
- {file = "nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:8878dce784d0fac90131b6817b607e803c36e629ba34dc5b433471382196b6a5"},
- {file = "nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623"},
- {file = "nvidia_cusparselt_cu12-0.7.1-py3-none-win_amd64.whl", hash = "sha256:f67fbb5831940ec829c9117b7f33807db9f9678dc2a617fbe781cac17b4e1075"},
-]
-
-[[package]]
-name = "nvidia-nccl-cu12"
-version = "2.26.2"
-description = "NVIDIA Collective Communication Library (NCCL) Runtime"
-optional = false
-python-versions = ">=3"
-groups = ["main"]
-markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.14\""
-files = [
- {file = "nvidia_nccl_cu12-2.26.2-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5c196e95e832ad30fbbb50381eb3cbd1fadd5675e587a548563993609af19522"},
- {file = "nvidia_nccl_cu12-2.26.2-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:694cf3879a206553cc9d7dbda76b13efaf610fdb70a50cba303de1b0d1530ac6"},
-]
-
-[[package]]
-name = "nvidia-nccl-cu12"
-version = "2.27.3"
-description = "NVIDIA Collective Communication Library (NCCL) Runtime"
-optional = false
-python-versions = ">=3"
-groups = ["main"]
-markers = "python_version < \"3.14\" and platform_machine == \"x86_64\" and platform_system == \"Linux\""
-files = [
- {file = "nvidia_nccl_cu12-2.27.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9ddf1a245abc36c550870f26d537a9b6087fb2e2e3d6e0ef03374c6fd19d984f"},
- {file = "nvidia_nccl_cu12-2.27.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adf27ccf4238253e0b826bce3ff5fa532d65fc42322c8bfdfaf28024c0fbe039"},
-]
-
-[[package]]
-name = "nvidia-nvjitlink-cu12"
-version = "12.6.85"
-description = "Nvidia JIT LTO Library"
-optional = false
-python-versions = ">=3"
-groups = ["main"]
-markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.14\""
-files = [
- {file = "nvidia_nvjitlink_cu12-12.6.85-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:eedc36df9e88b682efe4309aa16b5b4e78c2407eac59e8c10a6a47535164369a"},
- {file = "nvidia_nvjitlink_cu12-12.6.85-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cf4eaa7d4b6b543ffd69d6abfb11efdeb2db48270d94dfd3a452c24150829e41"},
- {file = "nvidia_nvjitlink_cu12-12.6.85-py3-none-win_amd64.whl", hash = "sha256:e61120e52ed675747825cdd16febc6a0730537451d867ee58bee3853b1b13d1c"},
-]
-
-[[package]]
-name = "nvidia-nvjitlink-cu12"
-version = "12.8.93"
-description = "Nvidia JIT LTO Library"
-optional = false
-python-versions = ">=3"
-groups = ["main"]
-markers = "python_version < \"3.14\" and platform_machine == \"x86_64\" and platform_system == \"Linux\""
-files = [
- {file = "nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88"},
- {file = "nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:adccd7161ace7261e01bb91e44e88da350895c270d23f744f0820c818b7229e7"},
- {file = "nvidia_nvjitlink_cu12-12.8.93-py3-none-win_amd64.whl", hash = "sha256:bd93fbeeee850917903583587f4fc3a4eafa022e34572251368238ab5e6bd67f"},
-]
-
-[[package]]
-name = "nvidia-nvtx-cu12"
-version = "12.6.77"
-description = "NVIDIA Tools Extension"
-optional = false
-python-versions = ">=3"
-groups = ["main"]
-markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.14\""
-files = [
- {file = "nvidia_nvtx_cu12-12.6.77-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f44f8d86bb7d5629988d61c8d3ae61dddb2015dee142740536bc7481b022fe4b"},
- {file = "nvidia_nvtx_cu12-12.6.77-py3-none-manylinux2014_aarch64.whl", hash = "sha256:adcaabb9d436c9761fca2b13959a2d237c5f9fd406c8e4b723c695409ff88059"},
- {file = "nvidia_nvtx_cu12-12.6.77-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b90bed3df379fa79afbd21be8e04a0314336b8ae16768b58f2d34cb1d04cd7d2"},
- {file = "nvidia_nvtx_cu12-12.6.77-py3-none-manylinux2014_x86_64.whl", hash = "sha256:6574241a3ec5fdc9334353ab8c479fe75841dbe8f4532a8fc97ce63503330ba1"},
- {file = "nvidia_nvtx_cu12-12.6.77-py3-none-win_amd64.whl", hash = "sha256:2fb11a4af04a5e6c84073e6404d26588a34afd35379f0855a99797897efa75c0"},
-]
-
-[[package]]
-name = "nvidia-nvtx-cu12"
-version = "12.8.90"
-description = "NVIDIA Tools Extension"
-optional = false
-python-versions = ">=3"
-groups = ["main"]
-markers = "python_version < \"3.14\" and platform_machine == \"x86_64\" and platform_system == \"Linux\""
-files = [
- {file = "nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d7ad891da111ebafbf7e015d34879f7112832fc239ff0d7d776b6cb685274615"},
- {file = "nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f"},
- {file = "nvidia_nvtx_cu12-12.8.90-py3-none-win_amd64.whl", hash = "sha256:619c8304aedc69f02ea82dd244541a83c3d9d40993381b3b590f1adaed3db41e"},
-]
-
-[[package]]
-name = "opencv-python-headless"
-version = "4.11.0.86"
-description = "Wrapper package for OpenCV python bindings."
-optional = false
-python-versions = ">=3.6"
-groups = ["main"]
-files = [
- {file = "opencv-python-headless-4.11.0.86.tar.gz", hash = "sha256:996eb282ca4b43ec6a3972414de0e2331f5d9cda2b41091a49739c19fb843798"},
- {file = "opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:48128188ade4a7e517237c8e1e11a9cdf5c282761473383e77beb875bb1e61ca"},
- {file = "opencv_python_headless-4.11.0.86-cp37-abi3-macosx_13_0_x86_64.whl", hash = "sha256:a66c1b286a9de872c343ee7c3553b084244299714ebb50fbdcd76f07ebbe6c81"},
- {file = "opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6efabcaa9df731f29e5ea9051776715b1bdd1845d7c9530065c7951d2a2899eb"},
- {file = "opencv_python_headless-4.11.0.86-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e0a27c19dd1f40ddff94976cfe43066fbbe9dfbb2ec1907d66c19caef42a57b"},
- {file = "opencv_python_headless-4.11.0.86-cp37-abi3-win32.whl", hash = "sha256:f447d8acbb0b6f2808da71fddd29c1cdd448d2bc98f72d9bb78a7a898fc9621b"},
- {file = "opencv_python_headless-4.11.0.86-cp37-abi3-win_amd64.whl", hash = "sha256:6c304df9caa7a6a5710b91709dd4786bf20a74d57672b3c31f7033cc638174ca"},
-]
-
-[package.dependencies]
-numpy = [
- {version = ">=1.26.0", markers = "python_version >= \"3.12\""},
- {version = ">=1.23.5", markers = "python_version == \"3.11\""},
- {version = ">=1.21.4", markers = "python_version == \"3.10\" and platform_system == \"Darwin\""},
- {version = ">=1.21.2", markers = "platform_system != \"Darwin\" and python_version == \"3.10\""},
-]
-
-[[package]]
-name = "overrides"
-version = "7.7.0"
-description = "A decorator to automatically detect mismatch when overriding a method."
-optional = false
-python-versions = ">=3.6"
-groups = ["dev"]
-markers = "python_version == \"3.10\" or python_version == \"3.11\""
-files = [
- {file = "overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49"},
- {file = "overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a"},
-]
-
-[[package]]
-name = "packaging"
-version = "25.0"
-description = "Core utilities for Python packages"
-optional = false
-python-versions = ">=3.8"
-groups = ["main", "dev", "xla"]
-files = [
- {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"},
- {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"},
-]
-
-[[package]]
-name = "pandas"
-version = "2.3.2"
-description = "Powerful data structures for data analysis, time series, and statistics"
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "pandas-2.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52bc29a946304c360561974c6542d1dd628ddafa69134a7131fdfd6a5d7a1a35"},
- {file = "pandas-2.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:220cc5c35ffaa764dd5bb17cf42df283b5cb7fdf49e10a7b053a06c9cb48ee2b"},
- {file = "pandas-2.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42c05e15111221384019897df20c6fe893b2f697d03c811ee67ec9e0bb5a3424"},
- {file = "pandas-2.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc03acc273c5515ab69f898df99d9d4f12c4d70dbfc24c3acc6203751d0804cf"},
- {file = "pandas-2.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d25c20a03e8870f6339bcf67281b946bd20b86f1a544ebbebb87e66a8d642cba"},
- {file = "pandas-2.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21bb612d148bb5860b7eb2c10faacf1a810799245afd342cf297d7551513fbb6"},
- {file = "pandas-2.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:b62d586eb25cb8cb70a5746a378fc3194cb7f11ea77170d59f889f5dfe3cec7a"},
- {file = "pandas-2.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1333e9c299adcbb68ee89a9bb568fc3f20f9cbb419f1dd5225071e6cddb2a743"},
- {file = "pandas-2.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:76972bcbd7de8e91ad5f0ca884a9f2c477a2125354af624e022c49e5bd0dfff4"},
- {file = "pandas-2.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b98bdd7c456a05eef7cd21fd6b29e3ca243591fe531c62be94a2cc987efb5ac2"},
- {file = "pandas-2.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d81573b3f7db40d020983f78721e9bfc425f411e616ef019a10ebf597aedb2e"},
- {file = "pandas-2.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e190b738675a73b581736cc8ec71ae113d6c3768d0bd18bffa5b9a0927b0b6ea"},
- {file = "pandas-2.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:c253828cb08f47488d60f43c5fc95114c771bbfff085da54bfc79cb4f9e3a372"},
- {file = "pandas-2.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:9467697b8083f9667b212633ad6aa4ab32436dcbaf4cd57325debb0ddef2012f"},
- {file = "pandas-2.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fbb977f802156e7a3f829e9d1d5398f6192375a3e2d1a9ee0803e35fe70a2b9"},
- {file = "pandas-2.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1b9b52693123dd234b7c985c68b709b0b009f4521000d0525f2b95c22f15944b"},
- {file = "pandas-2.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bd281310d4f412733f319a5bc552f86d62cddc5f51d2e392c8787335c994175"},
- {file = "pandas-2.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96d31a6b4354e3b9b8a2c848af75d31da390657e3ac6f30c05c82068b9ed79b9"},
- {file = "pandas-2.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:df4df0b9d02bb873a106971bb85d448378ef14b86ba96f035f50bbd3688456b4"},
- {file = "pandas-2.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:213a5adf93d020b74327cb2c1b842884dbdd37f895f42dcc2f09d451d949f811"},
- {file = "pandas-2.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:8c13b81a9347eb8c7548f53fd9a4f08d4dfe996836543f805c987bafa03317ae"},
- {file = "pandas-2.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0c6ecbac99a354a051ef21c5307601093cb9e0f4b1855984a084bfec9302699e"},
- {file = "pandas-2.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c6f048aa0fd080d6a06cc7e7537c09b53be6642d330ac6f54a600c3ace857ee9"},
- {file = "pandas-2.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0064187b80a5be6f2f9c9d6bdde29372468751dfa89f4211a3c5871854cfbf7a"},
- {file = "pandas-2.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ac8c320bded4718b298281339c1a50fb00a6ba78cb2a63521c39bec95b0209b"},
- {file = "pandas-2.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:114c2fe4f4328cf98ce5716d1532f3ab79c5919f95a9cfee81d9140064a2e4d6"},
- {file = "pandas-2.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:48fa91c4dfb3b2b9bfdb5c24cd3567575f4e13f9636810462ffed8925352be5a"},
- {file = "pandas-2.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:12d039facec710f7ba305786837d0225a3444af7bbd9c15c32ca2d40d157ed8b"},
- {file = "pandas-2.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:c624b615ce97864eb588779ed4046186f967374185c047070545253a52ab2d57"},
- {file = "pandas-2.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:0cee69d583b9b128823d9514171cabb6861e09409af805b54459bd0c821a35c2"},
- {file = "pandas-2.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2319656ed81124982900b4c37f0e0c58c015af9a7bbc62342ba5ad07ace82ba9"},
- {file = "pandas-2.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b37205ad6f00d52f16b6d09f406434ba928c1a1966e2771006a9033c736d30d2"},
- {file = "pandas-2.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:837248b4fc3a9b83b9c6214699a13f069dc13510a6a6d7f9ba33145d2841a012"},
- {file = "pandas-2.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d2c3554bd31b731cd6490d94a28f3abb8dd770634a9e06eb6d2911b9827db370"},
- {file = "pandas-2.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:88080a0ff8a55eac9c84e3ff3c7665b3b5476c6fbc484775ca1910ce1c3e0b87"},
- {file = "pandas-2.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d4a558c7620340a0931828d8065688b3cc5b4c8eb674bcaf33d18ff4a6870b4a"},
- {file = "pandas-2.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45178cf09d1858a1509dc73ec261bf5b25a625a389b65be2e47b559905f0ab6a"},
- {file = "pandas-2.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77cefe00e1b210f9c76c697fedd8fdb8d3dd86563e9c8adc9fa72b90f5e9e4c2"},
- {file = "pandas-2.3.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:13bd629c653856f00c53dc495191baa59bcafbbf54860a46ecc50d3a88421a96"},
- {file = "pandas-2.3.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:36d627906fd44b5fd63c943264e11e96e923f8de77d6016dc2f667b9ad193438"},
- {file = "pandas-2.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:a9d7ec92d71a420185dec44909c32e9a362248c4ae2238234b76d5be37f208cc"},
- {file = "pandas-2.3.2.tar.gz", hash = "sha256:ab7b58f8f82706890924ccdfb5f48002b83d2b5a3845976a9fb705d36c34dcdb"},
-]
-
-[package.dependencies]
-numpy = [
- {version = ">=1.26.0", markers = "python_version >= \"3.12\""},
- {version = ">=1.23.2", markers = "python_version == \"3.11\""},
- {version = ">=1.22.4", markers = "python_version < \"3.11\""},
-]
-python-dateutil = ">=2.8.2"
-pytz = ">=2020.1"
-tzdata = ">=2022.7"
-
-[package.extras]
-all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)", "beautifulsoup4 (>=4.11.2)", "bottleneck (>=1.3.6)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=2022.12.0)", "fsspec (>=2022.11.0)", "gcsfs (>=2022.11.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.9.2)", "matplotlib (>=3.6.3)", "numba (>=0.56.4)", "numexpr (>=2.8.4)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "pandas-gbq (>=0.19.0)", "psycopg2 (>=2.9.6)", "pyarrow (>=10.0.1)", "pymysql (>=1.0.2)", "pyreadstat (>=1.2.0)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "qtpy (>=2.3.0)", "s3fs (>=2022.11.0)", "scipy (>=1.10.0)", "tables (>=3.8.0)", "tabulate (>=0.9.0)", "xarray (>=2022.12.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)", "zstandard (>=0.19.0)"]
-aws = ["s3fs (>=2022.11.0)"]
-clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.3.0)"]
-compression = ["zstandard (>=0.19.0)"]
-computation = ["scipy (>=1.10.0)", "xarray (>=2022.12.0)"]
-consortium-standard = ["dataframe-api-compat (>=0.1.7)"]
-excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)"]
-feather = ["pyarrow (>=10.0.1)"]
-fss = ["fsspec (>=2022.11.0)"]
-gcp = ["gcsfs (>=2022.11.0)", "pandas-gbq (>=0.19.0)"]
-hdf5 = ["tables (>=3.8.0)"]
-html = ["beautifulsoup4 (>=4.11.2)", "html5lib (>=1.1)", "lxml (>=4.9.2)"]
-mysql = ["SQLAlchemy (>=2.0.0)", "pymysql (>=1.0.2)"]
-output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.9.0)"]
-parquet = ["pyarrow (>=10.0.1)"]
-performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"]
-plot = ["matplotlib (>=3.6.3)"]
-postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"]
-pyarrow = ["pyarrow (>=10.0.1)"]
-spss = ["pyreadstat (>=1.2.0)"]
-sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"]
-test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"]
-xml = ["lxml (>=4.9.2)"]
-
-[[package]]
-name = "pandocfilters"
-version = "1.5.1"
-description = "Utilities for writing pandoc filters in python"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-groups = ["dev"]
-files = [
- {file = "pandocfilters-1.5.1-py2.py3-none-any.whl", hash = "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc"},
- {file = "pandocfilters-1.5.1.tar.gz", hash = "sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e"},
-]
-
-[[package]]
-name = "parso"
-version = "0.8.5"
-description = "A Python Parser"
-optional = false
-python-versions = ">=3.6"
-groups = ["dev"]
-files = [
- {file = "parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887"},
- {file = "parso-0.8.5.tar.gz", hash = "sha256:034d7354a9a018bdce352f48b2a8a450f05e9d6ee85db84764e9b6bd96dafe5a"},
-]
-
-[package.extras]
-qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"]
-testing = ["docopt", "pytest"]
-
-[[package]]
-name = "pdftext"
-version = "0.5.1"
-description = "Extract structured text from pdfs quickly"
-optional = false
-python-versions = "<4.0,>=3.10"
-groups = ["dev"]
-files = [
- {file = "pdftext-0.5.1-py3-none-any.whl", hash = "sha256:6de0406473846f6486b969fb4b1832b94ebe4c92a4bae5f3d1ead645d43d9994"},
- {file = "pdftext-0.5.1.tar.gz", hash = "sha256:81646068c98df4874064f739f507908543188e93e1a5d84b30a0989329f32af6"},
-]
-
-[package.dependencies]
-click = ">=8.1.8,<9.0.0"
-pydantic = ">=2.7.1,<3.0.0"
-pydantic-settings = ">=2.2.1,<3.0.0"
-pypdfium2 = "4.30.0"
-
-[[package]]
-name = "pexpect"
-version = "4.9.0"
-description = "Pexpect allows easy control of interactive console applications."
-optional = false
-python-versions = "*"
-groups = ["dev"]
-markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""
-files = [
- {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"},
- {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"},
-]
-
-[package.dependencies]
-ptyprocess = ">=0.5"
-
-[[package]]
-name = "pillow"
-version = "10.4.0"
-description = "Python Imaging Library (Fork)"
-optional = false
-python-versions = ">=3.8"
-groups = ["main", "dev"]
-files = [
- {file = "pillow-10.4.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e"},
- {file = "pillow-10.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d"},
- {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856"},
- {file = "pillow-10.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f"},
- {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b"},
- {file = "pillow-10.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc"},
- {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e"},
- {file = "pillow-10.4.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46"},
- {file = "pillow-10.4.0-cp310-cp310-win32.whl", hash = "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984"},
- {file = "pillow-10.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141"},
- {file = "pillow-10.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1"},
- {file = "pillow-10.4.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c"},
- {file = "pillow-10.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be"},
- {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3"},
- {file = "pillow-10.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6"},
- {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe"},
- {file = "pillow-10.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319"},
- {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d"},
- {file = "pillow-10.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696"},
- {file = "pillow-10.4.0-cp311-cp311-win32.whl", hash = "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496"},
- {file = "pillow-10.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91"},
- {file = "pillow-10.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22"},
- {file = "pillow-10.4.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94"},
- {file = "pillow-10.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597"},
- {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80"},
- {file = "pillow-10.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca"},
- {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef"},
- {file = "pillow-10.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a"},
- {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b"},
- {file = "pillow-10.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9"},
- {file = "pillow-10.4.0-cp312-cp312-win32.whl", hash = "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42"},
- {file = "pillow-10.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a"},
- {file = "pillow-10.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9"},
- {file = "pillow-10.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3"},
- {file = "pillow-10.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb"},
- {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70"},
- {file = "pillow-10.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be"},
- {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0"},
- {file = "pillow-10.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc"},
- {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a"},
- {file = "pillow-10.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309"},
- {file = "pillow-10.4.0-cp313-cp313-win32.whl", hash = "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060"},
- {file = "pillow-10.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea"},
- {file = "pillow-10.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d"},
- {file = "pillow-10.4.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8d4d5063501b6dd4024b8ac2f04962d661222d120381272deea52e3fc52d3736"},
- {file = "pillow-10.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7c1ee6f42250df403c5f103cbd2768a28fe1a0ea1f0f03fe151c8741e1469c8b"},
- {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15e02e9bb4c21e39876698abf233c8c579127986f8207200bc8a8f6bb27acf2"},
- {file = "pillow-10.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a8d4bade9952ea9a77d0c3e49cbd8b2890a399422258a77f357b9cc9be8d680"},
- {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:43efea75eb06b95d1631cb784aa40156177bf9dd5b4b03ff38979e048258bc6b"},
- {file = "pillow-10.4.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:950be4d8ba92aca4b2bb0741285a46bfae3ca699ef913ec8416c1b78eadd64cd"},
- {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d7480af14364494365e89d6fddc510a13e5a2c3584cb19ef65415ca57252fb84"},
- {file = "pillow-10.4.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:73664fe514b34c8f02452ffb73b7a92c6774e39a647087f83d67f010eb9a0cf0"},
- {file = "pillow-10.4.0-cp38-cp38-win32.whl", hash = "sha256:e88d5e6ad0d026fba7bdab8c3f225a69f063f116462c49892b0149e21b6c0a0e"},
- {file = "pillow-10.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:5161eef006d335e46895297f642341111945e2c1c899eb406882a6c61a4357ab"},
- {file = "pillow-10.4.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0ae24a547e8b711ccaaf99c9ae3cd975470e1a30caa80a6aaee9a2f19c05701d"},
- {file = "pillow-10.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:298478fe4f77a4408895605f3482b6cc6222c018b2ce565c2b6b9c354ac3229b"},
- {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:134ace6dc392116566980ee7436477d844520a26a4b1bd4053f6f47d096997fd"},
- {file = "pillow-10.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:930044bb7679ab003b14023138b50181899da3f25de50e9dbee23b61b4de2126"},
- {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c76e5786951e72ed3686e122d14c5d7012f16c8303a674d18cdcd6d89557fc5b"},
- {file = "pillow-10.4.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b2724fdb354a868ddf9a880cb84d102da914e99119211ef7ecbdc613b8c96b3c"},
- {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dbc6ae66518ab3c5847659e9988c3b60dc94ffb48ef9168656e0019a93dbf8a1"},
- {file = "pillow-10.4.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:06b2f7898047ae93fad74467ec3d28fe84f7831370e3c258afa533f81ef7f3df"},
- {file = "pillow-10.4.0-cp39-cp39-win32.whl", hash = "sha256:7970285ab628a3779aecc35823296a7869f889b8329c16ad5a71e4901a3dc4ef"},
- {file = "pillow-10.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:961a7293b2457b405967af9c77dcaa43cc1a8cd50d23c532e62d48ab6cdd56f5"},
- {file = "pillow-10.4.0-cp39-cp39-win_arm64.whl", hash = "sha256:32cda9e3d601a52baccb2856b8ea1fc213c90b340c542dcef77140dfa3278a9e"},
- {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4"},
- {file = "pillow-10.4.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da"},
- {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026"},
- {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e"},
- {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5"},
- {file = "pillow-10.4.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885"},
- {file = "pillow-10.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5"},
- {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:a02364621fe369e06200d4a16558e056fe2805d3468350df3aef21e00d26214b"},
- {file = "pillow-10.4.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1b5dea9831a90e9d0721ec417a80d4cbd7022093ac38a568db2dd78363b00908"},
- {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b885f89040bb8c4a1573566bbb2f44f5c505ef6e74cec7ab9068c900047f04b"},
- {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87dd88ded2e6d74d31e1e0a99a726a6765cda32d00ba72dc37f0651f306daaa8"},
- {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:2db98790afc70118bd0255c2eeb465e9767ecf1f3c25f9a1abb8ffc8cfd1fe0a"},
- {file = "pillow-10.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27"},
- {file = "pillow-10.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:cfdd747216947628af7b259d274771d84db2268ca062dd5faf373639d00113a3"},
- {file = "pillow-10.4.0.tar.gz", hash = "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06"},
-]
-
-[package.extras]
-docs = ["furo", "olefile", "sphinx (>=7.3)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinxext-opengraph"]
-fpx = ["olefile"]
-mic = ["olefile"]
-tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"]
-typing = ["typing-extensions ; python_version < \"3.10\""]
-xmp = ["defusedxml"]
-
-[[package]]
-name = "platformdirs"
-version = "4.4.0"
-description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`."
-optional = false
-python-versions = ">=3.9"
-groups = ["main", "dev"]
-files = [
- {file = "platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85"},
- {file = "platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf"},
-]
-
-[package.extras]
-docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"]
-test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"]
-type = ["mypy (>=1.14.1)"]
-
-[[package]]
-name = "pluggy"
-version = "1.6.0"
-description = "plugin and hook calling mechanisms for python"
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"},
- {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"},
-]
-
-[package.extras]
-dev = ["pre-commit", "tox"]
-testing = ["coverage", "pytest", "pytest-benchmark"]
-
-[[package]]
-name = "pre-commit"
-version = "4.3.0"
-description = "A framework for managing and maintaining multi-language pre-commit hooks."
-optional = false
-python-versions = ">=3.9"
-groups = ["main"]
-files = [
- {file = "pre_commit-4.3.0-py2.py3-none-any.whl", hash = "sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8"},
- {file = "pre_commit-4.3.0.tar.gz", hash = "sha256:499fe450cc9d42e9d58e606262795ecb64dd05438943c62b66f6a8673da30b16"},
-]
-
-[package.dependencies]
-cfgv = ">=2.0.0"
-identify = ">=1.0.0"
-nodeenv = ">=0.11.1"
-pyyaml = ">=5.1"
-virtualenv = ">=20.10.0"
-
-[[package]]
-name = "prometheus-client"
-version = "0.22.1"
-description = "Python client for the Prometheus monitoring system."
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "prometheus_client-0.22.1-py3-none-any.whl", hash = "sha256:cca895342e308174341b2cbf99a56bef291fbc0ef7b9e5412a0f26d653ba7094"},
- {file = "prometheus_client-0.22.1.tar.gz", hash = "sha256:190f1331e783cf21eb60bca559354e0a4d4378facecf78f5428c39b675d20d28"},
-]
-
-[package.extras]
-twisted = ["twisted"]
-
-[[package]]
-name = "prompt-toolkit"
-version = "3.0.52"
-description = "Library for building powerful interactive command lines in Python"
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955"},
- {file = "prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855"},
-]
-
-[package.dependencies]
-wcwidth = "*"
-
-[[package]]
-name = "propcache"
-version = "0.3.2"
-description = "Accelerated property cache"
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "propcache-0.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:22d9962a358aedbb7a2e36187ff273adeaab9743373a272976d2e348d08c7770"},
- {file = "propcache-0.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d0fda578d1dc3f77b6b5a5dce3b9ad69a8250a891760a548df850a5e8da87f3"},
- {file = "propcache-0.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3def3da3ac3ce41562d85db655d18ebac740cb3fa4367f11a52b3da9d03a5cc3"},
- {file = "propcache-0.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9bec58347a5a6cebf239daba9bda37dffec5b8d2ce004d9fe4edef3d2815137e"},
- {file = "propcache-0.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55ffda449a507e9fbd4aca1a7d9aa6753b07d6166140e5a18d2ac9bc49eac220"},
- {file = "propcache-0.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64a67fb39229a8a8491dd42f864e5e263155e729c2e7ff723d6e25f596b1e8cb"},
- {file = "propcache-0.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9da1cf97b92b51253d5b68cf5a2b9e0dafca095e36b7f2da335e27dc6172a614"},
- {file = "propcache-0.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5f559e127134b07425134b4065be45b166183fdcb433cb6c24c8e4149056ad50"},
- {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:aff2e4e06435d61f11a428360a932138d0ec288b0a31dd9bd78d200bd4a2b339"},
- {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4927842833830942a5d0a56e6f4839bc484785b8e1ce8d287359794818633ba0"},
- {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:6107ddd08b02654a30fb8ad7a132021759d750a82578b94cd55ee2772b6ebea2"},
- {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:70bd8b9cd6b519e12859c99f3fc9a93f375ebd22a50296c3a295028bea73b9e7"},
- {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2183111651d710d3097338dd1893fcf09c9f54e27ff1a8795495a16a469cc90b"},
- {file = "propcache-0.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fb075ad271405dcad8e2a7ffc9a750a3bf70e533bd86e89f0603e607b93aa64c"},
- {file = "propcache-0.3.2-cp310-cp310-win32.whl", hash = "sha256:404d70768080d3d3bdb41d0771037da19d8340d50b08e104ca0e7f9ce55fce70"},
- {file = "propcache-0.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:7435d766f978b4ede777002e6b3b6641dd229cd1da8d3d3106a45770365f9ad9"},
- {file = "propcache-0.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:0b8d2f607bd8f80ddc04088bc2a037fdd17884a6fcadc47a96e334d72f3717be"},
- {file = "propcache-0.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06766d8f34733416e2e34f46fea488ad5d60726bb9481d3cddf89a6fa2d9603f"},
- {file = "propcache-0.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2dc1f4a1df4fecf4e6f68013575ff4af84ef6f478fe5344317a65d38a8e6dc9"},
- {file = "propcache-0.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be29c4f4810c5789cf10ddf6af80b041c724e629fa51e308a7a0fb19ed1ef7bf"},
- {file = "propcache-0.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59d61f6970ecbd8ff2e9360304d5c8876a6abd4530cb752c06586849ac8a9dc9"},
- {file = "propcache-0.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:62180e0b8dbb6b004baec00a7983e4cc52f5ada9cd11f48c3528d8cfa7b96a66"},
- {file = "propcache-0.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c144ca294a204c470f18cf4c9d78887810d04a3e2fbb30eea903575a779159df"},
- {file = "propcache-0.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c5c2a784234c28854878d68978265617aa6dc0780e53d44b4d67f3651a17a9a2"},
- {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5745bc7acdafa978ca1642891b82c19238eadc78ba2aaa293c6863b304e552d7"},
- {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:c0075bf773d66fa8c9d41f66cc132ecc75e5bb9dd7cce3cfd14adc5ca184cb95"},
- {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5f57aa0847730daceff0497f417c9de353c575d8da3579162cc74ac294c5369e"},
- {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:eef914c014bf72d18efb55619447e0aecd5fb7c2e3fa7441e2e5d6099bddff7e"},
- {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2a4092e8549031e82facf3decdbc0883755d5bbcc62d3aea9d9e185549936dcf"},
- {file = "propcache-0.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:85871b050f174bc0bfb437efbdb68aaf860611953ed12418e4361bc9c392749e"},
- {file = "propcache-0.3.2-cp311-cp311-win32.whl", hash = "sha256:36c8d9b673ec57900c3554264e630d45980fd302458e4ac801802a7fd2ef7897"},
- {file = "propcache-0.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53af8cb6a781b02d2ea079b5b853ba9430fcbe18a8e3ce647d5982a3ff69f39"},
- {file = "propcache-0.3.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:8de106b6c84506b31c27168582cd3cb3000a6412c16df14a8628e5871ff83c10"},
- {file = "propcache-0.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:28710b0d3975117239c76600ea351934ac7b5ff56e60953474342608dbbb6154"},
- {file = "propcache-0.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce26862344bdf836650ed2487c3d724b00fbfec4233a1013f597b78c1cb73615"},
- {file = "propcache-0.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bca54bd347a253af2cf4544bbec232ab982f4868de0dd684246b67a51bc6b1db"},
- {file = "propcache-0.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55780d5e9a2ddc59711d727226bb1ba83a22dd32f64ee15594b9392b1f544eb1"},
- {file = "propcache-0.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:035e631be25d6975ed87ab23153db6a73426a48db688070d925aa27e996fe93c"},
- {file = "propcache-0.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee6f22b6eaa39297c751d0e80c0d3a454f112f5c6481214fcf4c092074cecd67"},
- {file = "propcache-0.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7ca3aee1aa955438c4dba34fc20a9f390e4c79967257d830f137bd5a8a32ed3b"},
- {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7a4f30862869fa2b68380d677cc1c5fcf1e0f2b9ea0cf665812895c75d0ca3b8"},
- {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b77ec3c257d7816d9f3700013639db7491a434644c906a2578a11daf13176251"},
- {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:cab90ac9d3f14b2d5050928483d3d3b8fb6b4018893fc75710e6aa361ecb2474"},
- {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0b504d29f3c47cf6b9e936c1852246c83d450e8e063d50562115a6be6d3a2535"},
- {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:ce2ac2675a6aa41ddb2a0c9cbff53780a617ac3d43e620f8fd77ba1c84dcfc06"},
- {file = "propcache-0.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:62b4239611205294cc433845b914131b2a1f03500ff3c1ed093ed216b82621e1"},
- {file = "propcache-0.3.2-cp312-cp312-win32.whl", hash = "sha256:df4a81b9b53449ebc90cc4deefb052c1dd934ba85012aa912c7ea7b7e38b60c1"},
- {file = "propcache-0.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:7046e79b989d7fe457bb755844019e10f693752d169076138abf17f31380800c"},
- {file = "propcache-0.3.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ca592ed634a73ca002967458187109265e980422116c0a107cf93d81f95af945"},
- {file = "propcache-0.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9ecb0aad4020e275652ba3975740f241bd12a61f1a784df044cf7477a02bc252"},
- {file = "propcache-0.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7f08f1cc28bd2eade7a8a3d2954ccc673bb02062e3e7da09bc75d843386b342f"},
- {file = "propcache-0.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1a342c834734edb4be5ecb1e9fb48cb64b1e2320fccbd8c54bf8da8f2a84c33"},
- {file = "propcache-0.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a544caaae1ac73f1fecfae70ded3e93728831affebd017d53449e3ac052ac1e"},
- {file = "propcache-0.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:310d11aa44635298397db47a3ebce7db99a4cc4b9bbdfcf6c98a60c8d5261cf1"},
- {file = "propcache-0.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c1396592321ac83157ac03a2023aa6cc4a3cc3cfdecb71090054c09e5a7cce3"},
- {file = "propcache-0.3.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cabf5b5902272565e78197edb682017d21cf3b550ba0460ee473753f28d23c1"},
- {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0a2f2235ac46a7aa25bdeb03a9e7060f6ecbd213b1f9101c43b3090ffb971ef6"},
- {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:92b69e12e34869a6970fd2f3da91669899994b47c98f5d430b781c26f1d9f387"},
- {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:54e02207c79968ebbdffc169591009f4474dde3b4679e16634d34c9363ff56b4"},
- {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4adfb44cb588001f68c5466579d3f1157ca07f7504fc91ec87862e2b8e556b88"},
- {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:fd3e6019dc1261cd0291ee8919dd91fbab7b169bb76aeef6c716833a3f65d206"},
- {file = "propcache-0.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4c181cad81158d71c41a2bce88edce078458e2dd5ffee7eddd6b05da85079f43"},
- {file = "propcache-0.3.2-cp313-cp313-win32.whl", hash = "sha256:8a08154613f2249519e549de2330cf8e2071c2887309a7b07fb56098f5170a02"},
- {file = "propcache-0.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e41671f1594fc4ab0a6dec1351864713cb3a279910ae8b58f884a88a0a632c05"},
- {file = "propcache-0.3.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:9a3cf035bbaf035f109987d9d55dc90e4b0e36e04bbbb95af3055ef17194057b"},
- {file = "propcache-0.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:156c03d07dc1323d8dacaa221fbe028c5c70d16709cdd63502778e6c3ccca1b0"},
- {file = "propcache-0.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:74413c0ba02ba86f55cf60d18daab219f7e531620c15f1e23d95563f505efe7e"},
- {file = "propcache-0.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f066b437bb3fa39c58ff97ab2ca351db465157d68ed0440abecb21715eb24b28"},
- {file = "propcache-0.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f1304b085c83067914721e7e9d9917d41ad87696bf70f0bc7dee450e9c71ad0a"},
- {file = "propcache-0.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ab50cef01b372763a13333b4e54021bdcb291fc9a8e2ccb9c2df98be51bcde6c"},
- {file = "propcache-0.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fad3b2a085ec259ad2c2842666b2a0a49dea8463579c606426128925af1ed725"},
- {file = "propcache-0.3.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:261fa020c1c14deafd54c76b014956e2f86991af198c51139faf41c4d5e83892"},
- {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:46d7f8aa79c927e5f987ee3a80205c987717d3659f035c85cf0c3680526bdb44"},
- {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:6d8f3f0eebf73e3c0ff0e7853f68be638b4043c65a70517bb575eff54edd8dbe"},
- {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:03c89c1b14a5452cf15403e291c0ccd7751d5b9736ecb2c5bab977ad6c5bcd81"},
- {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:0cc17efde71e12bbaad086d679ce575268d70bc123a5a71ea7ad76f70ba30bba"},
- {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:acdf05d00696bc0447e278bb53cb04ca72354e562cf88ea6f9107df8e7fd9770"},
- {file = "propcache-0.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4445542398bd0b5d32df908031cb1b30d43ac848e20470a878b770ec2dcc6330"},
- {file = "propcache-0.3.2-cp313-cp313t-win32.whl", hash = "sha256:f86e5d7cd03afb3a1db8e9f9f6eff15794e79e791350ac48a8c924e6f439f394"},
- {file = "propcache-0.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:9704bedf6e7cbe3c65eca4379a9b53ee6a83749f047808cbb5044d40d7d72198"},
- {file = "propcache-0.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a7fad897f14d92086d6b03fdd2eb844777b0c4d7ec5e3bac0fbae2ab0602bbe5"},
- {file = "propcache-0.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1f43837d4ca000243fd7fd6301947d7cb93360d03cd08369969450cc6b2ce3b4"},
- {file = "propcache-0.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:261df2e9474a5949c46e962065d88eb9b96ce0f2bd30e9d3136bcde84befd8f2"},
- {file = "propcache-0.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e514326b79e51f0a177daab1052bc164d9d9e54133797a3a58d24c9c87a3fe6d"},
- {file = "propcache-0.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d4a996adb6904f85894570301939afeee65f072b4fd265ed7e569e8d9058e4ec"},
- {file = "propcache-0.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:76cace5d6b2a54e55b137669b30f31aa15977eeed390c7cbfb1dafa8dfe9a701"},
- {file = "propcache-0.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31248e44b81d59d6addbb182c4720f90b44e1efdc19f58112a3c3a1615fb47ef"},
- {file = "propcache-0.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abb7fa19dbf88d3857363e0493b999b8011eea856b846305d8c0512dfdf8fbb1"},
- {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d81ac3ae39d38588ad0549e321e6f773a4e7cc68e7751524a22885d5bbadf886"},
- {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:cc2782eb0f7a16462285b6f8394bbbd0e1ee5f928034e941ffc444012224171b"},
- {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:db429c19a6c7e8a1c320e6a13c99799450f411b02251fb1b75e6217cf4a14fcb"},
- {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:21d8759141a9e00a681d35a1f160892a36fb6caa715ba0b832f7747da48fb6ea"},
- {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2ca6d378f09adb13837614ad2754fa8afaee330254f404299611bce41a8438cb"},
- {file = "propcache-0.3.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:34a624af06c048946709f4278b4176470073deda88d91342665d95f7c6270fbe"},
- {file = "propcache-0.3.2-cp39-cp39-win32.whl", hash = "sha256:4ba3fef1c30f306b1c274ce0b8baaa2c3cdd91f645c48f06394068f37d3837a1"},
- {file = "propcache-0.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:7a2368eed65fc69a7a7a40b27f22e85e7627b74216f0846b04ba5c116e191ec9"},
- {file = "propcache-0.3.2-py3-none-any.whl", hash = "sha256:98f1ec44fb675f5052cccc8e609c46ed23a35a1cfd18545ad4e29002d858a43f"},
- {file = "propcache-0.3.2.tar.gz", hash = "sha256:20d7d62e4e7ef05f221e0db2856b979540686342e7dd9973b815599c7057e168"},
-]
-
-[[package]]
-name = "protobuf"
-version = "6.32.0"
-description = ""
-optional = false
-python-versions = ">=3.9"
-groups = ["dev", "xla"]
-files = [
- {file = "protobuf-6.32.0-cp310-abi3-win32.whl", hash = "sha256:84f9e3c1ff6fb0308dbacb0950d8aa90694b0d0ee68e75719cb044b7078fe741"},
- {file = "protobuf-6.32.0-cp310-abi3-win_amd64.whl", hash = "sha256:a8bdbb2f009cfc22a36d031f22a625a38b615b5e19e558a7b756b3279723e68e"},
- {file = "protobuf-6.32.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:d52691e5bee6c860fff9a1c86ad26a13afbeb4b168cd4445c922b7e2cf85aaf0"},
- {file = "protobuf-6.32.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:501fe6372fd1c8ea2a30b4d9be8f87955a64d6be9c88a973996cef5ef6f0abf1"},
- {file = "protobuf-6.32.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:75a2aab2bd1aeb1f5dc7c5f33bcb11d82ea8c055c9becbb41c26a8c43fd7092c"},
- {file = "protobuf-6.32.0-cp39-cp39-win32.whl", hash = "sha256:7db8ed09024f115ac877a1427557b838705359f047b2ff2f2b2364892d19dacb"},
- {file = "protobuf-6.32.0-cp39-cp39-win_amd64.whl", hash = "sha256:15eba1b86f193a407607112ceb9ea0ba9569aed24f93333fe9a497cf2fda37d3"},
- {file = "protobuf-6.32.0-py3-none-any.whl", hash = "sha256:ba377e5b67b908c8f3072a57b63e2c6a4cbd18aea4ed98d2584350dbf46f2783"},
- {file = "protobuf-6.32.0.tar.gz", hash = "sha256:a81439049127067fc49ec1d36e25c6ee1d1a2b7be930675f919258d03c04e7d2"},
-]
-
-[[package]]
-name = "psutil"
-version = "7.0.0"
-description = "Cross-platform lib for process and system monitoring in Python. NOTE: the syntax of this script MUST be kept compatible with Python 2.7."
-optional = false
-python-versions = ">=3.6"
-groups = ["dev"]
-files = [
- {file = "psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25"},
- {file = "psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da"},
- {file = "psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91"},
- {file = "psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34"},
- {file = "psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993"},
- {file = "psutil-7.0.0-cp36-cp36m-win32.whl", hash = "sha256:84df4eb63e16849689f76b1ffcb36db7b8de703d1bc1fe41773db487621b6c17"},
- {file = "psutil-7.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:1e744154a6580bc968a0195fd25e80432d3afec619daf145b9e5ba16cc1d688e"},
- {file = "psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99"},
- {file = "psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553"},
- {file = "psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456"},
-]
-
-[package.extras]
-dev = ["abi3audit", "black (==24.10.0)", "check-manifest", "coverage", "packaging", "pylint", "pyperf", "pypinfo", "pytest", "pytest-cov", "pytest-xdist", "requests", "rstcheck", "ruff", "setuptools", "sphinx", "sphinx_rtd_theme", "toml-sort", "twine", "virtualenv", "vulture", "wheel"]
-test = ["pytest", "pytest-xdist", "setuptools"]
-
-[[package]]
-name = "ptyprocess"
-version = "0.7.0"
-description = "Run a subprocess in a pseudo terminal"
-optional = false
-python-versions = "*"
-groups = ["dev"]
-markers = "os_name != \"nt\" or sys_platform != \"win32\" and sys_platform != \"emscripten\""
-files = [
- {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"},
- {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"},
-]
-
-[[package]]
-name = "pure-eval"
-version = "0.2.3"
-description = "Safely evaluate AST nodes without side effects"
-optional = false
-python-versions = "*"
-groups = ["dev"]
-files = [
- {file = "pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0"},
- {file = "pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42"},
-]
-
-[package.extras]
-tests = ["pytest"]
-
-[[package]]
-name = "pyarrow"
-version = "21.0.0"
-description = "Python library for Apache Arrow"
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "pyarrow-21.0.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:e563271e2c5ff4d4a4cbeb2c83d5cf0d4938b891518e676025f7268c6fe5fe26"},
- {file = "pyarrow-21.0.0-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:fee33b0ca46f4c85443d6c450357101e47d53e6c3f008d658c27a2d020d44c79"},
- {file = "pyarrow-21.0.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:7be45519b830f7c24b21d630a31d48bcebfd5d4d7f9d3bdb49da9cdf6d764edb"},
- {file = "pyarrow-21.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:26bfd95f6bff443ceae63c65dc7e048670b7e98bc892210acba7e4995d3d4b51"},
- {file = "pyarrow-21.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bd04ec08f7f8bd113c55868bd3fc442a9db67c27af098c5f814a3091e71cc61a"},
- {file = "pyarrow-21.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9b0b14b49ac10654332a805aedfc0147fb3469cbf8ea951b3d040dab12372594"},
- {file = "pyarrow-21.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:9d9f8bcb4c3be7738add259738abdeddc363de1b80e3310e04067aa1ca596634"},
- {file = "pyarrow-21.0.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:c077f48aab61738c237802836fc3844f85409a46015635198761b0d6a688f87b"},
- {file = "pyarrow-21.0.0-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:689f448066781856237eca8d1975b98cace19b8dd2ab6145bf49475478bcaa10"},
- {file = "pyarrow-21.0.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:479ee41399fcddc46159a551705b89c05f11e8b8cb8e968f7fec64f62d91985e"},
- {file = "pyarrow-21.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:40ebfcb54a4f11bcde86bc586cbd0272bac0d516cfa539c799c2453768477569"},
- {file = "pyarrow-21.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8d58d8497814274d3d20214fbb24abcad2f7e351474357d552a8d53bce70c70e"},
- {file = "pyarrow-21.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:585e7224f21124dd57836b1530ac8f2df2afc43c861d7bf3d58a4870c42ae36c"},
- {file = "pyarrow-21.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:555ca6935b2cbca2c0e932bedd853e9bc523098c39636de9ad4693b5b1df86d6"},
- {file = "pyarrow-21.0.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:3a302f0e0963db37e0a24a70c56cf91a4faa0bca51c23812279ca2e23481fccd"},
- {file = "pyarrow-21.0.0-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:b6b27cf01e243871390474a211a7922bfbe3bda21e39bc9160daf0da3fe48876"},
- {file = "pyarrow-21.0.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:e72a8ec6b868e258a2cd2672d91f2860ad532d590ce94cdf7d5e7ec674ccf03d"},
- {file = "pyarrow-21.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b7ae0bbdc8c6674259b25bef5d2a1d6af5d39d7200c819cf99e07f7dfef1c51e"},
- {file = "pyarrow-21.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:58c30a1729f82d201627c173d91bd431db88ea74dcaa3885855bc6203e433b82"},
- {file = "pyarrow-21.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:072116f65604b822a7f22945a7a6e581cfa28e3454fdcc6939d4ff6090126623"},
- {file = "pyarrow-21.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:cf56ec8b0a5c8c9d7021d6fd754e688104f9ebebf1bf4449613c9531f5346a18"},
- {file = "pyarrow-21.0.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:e99310a4ebd4479bcd1964dff9e14af33746300cb014aa4a3781738ac63baf4a"},
- {file = "pyarrow-21.0.0-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:d2fe8e7f3ce329a71b7ddd7498b3cfac0eeb200c2789bd840234f0dc271a8efe"},
- {file = "pyarrow-21.0.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:f522e5709379d72fb3da7785aa489ff0bb87448a9dc5a75f45763a795a089ebd"},
- {file = "pyarrow-21.0.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:69cbbdf0631396e9925e048cfa5bce4e8c3d3b41562bbd70c685a8eb53a91e61"},
- {file = "pyarrow-21.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:731c7022587006b755d0bdb27626a1a3bb004bb56b11fb30d98b6c1b4718579d"},
- {file = "pyarrow-21.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:dc56bc708f2d8ac71bd1dcb927e458c93cec10b98eb4120206a4091db7b67b99"},
- {file = "pyarrow-21.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:186aa00bca62139f75b7de8420f745f2af12941595bbbfa7ed3870ff63e25636"},
- {file = "pyarrow-21.0.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:a7a102574faa3f421141a64c10216e078df467ab9576684d5cd696952546e2da"},
- {file = "pyarrow-21.0.0-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:1e005378c4a2c6db3ada3ad4c217b381f6c886f0a80d6a316fe586b90f77efd7"},
- {file = "pyarrow-21.0.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:65f8e85f79031449ec8706b74504a316805217b35b6099155dd7e227eef0d4b6"},
- {file = "pyarrow-21.0.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:3a81486adc665c7eb1a2bde0224cfca6ceaba344a82a971ef059678417880eb8"},
- {file = "pyarrow-21.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:fc0d2f88b81dcf3ccf9a6ae17f89183762c8a94a5bdcfa09e05cfe413acf0503"},
- {file = "pyarrow-21.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6299449adf89df38537837487a4f8d3bd91ec94354fdd2a7d30bc11c48ef6e79"},
- {file = "pyarrow-21.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:222c39e2c70113543982c6b34f3077962b44fca38c0bd9e68bb6781534425c10"},
- {file = "pyarrow-21.0.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:a7f6524e3747e35f80744537c78e7302cd41deee8baa668d56d55f77d9c464b3"},
- {file = "pyarrow-21.0.0-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:203003786c9fd253ebcafa44b03c06983c9c8d06c3145e37f1b76a1f317aeae1"},
- {file = "pyarrow-21.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:3b4d97e297741796fead24867a8dabf86c87e4584ccc03167e4a811f50fdf74d"},
- {file = "pyarrow-21.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:898afce396b80fdda05e3086b4256f8677c671f7b1d27a6976fa011d3fd0a86e"},
- {file = "pyarrow-21.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:067c66ca29aaedae08218569a114e413b26e742171f526e828e1064fcdec13f4"},
- {file = "pyarrow-21.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0c4e75d13eb76295a49e0ea056eb18dbd87d81450bfeb8afa19a7e5a75ae2ad7"},
- {file = "pyarrow-21.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdc4c17afda4dab2a9c0b79148a43a7f4e1094916b3e18d8975bfd6d6d52241f"},
- {file = "pyarrow-21.0.0.tar.gz", hash = "sha256:5051f2dccf0e283ff56335760cbc8622cf52264d67e359d5569541ac11b6d5bc"},
-]
-
-[package.extras]
-test = ["cffi", "hypothesis", "pandas", "pytest", "pytz"]
-
-[[package]]
-name = "pycparser"
-version = "2.22"
-description = "C parser in Python"
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"},
- {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
-]
-
-[[package]]
-name = "pydantic"
-version = "2.11.7"
-description = "Data validation using Python type hints"
-optional = false
-python-versions = ">=3.9"
-groups = ["main", "dev"]
-files = [
- {file = "pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b"},
- {file = "pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db"},
-]
-
-[package.dependencies]
-annotated-types = ">=0.6.0"
-pydantic-core = "2.33.2"
-typing-extensions = ">=4.12.2"
-typing-inspection = ">=0.4.0"
-
-[package.extras]
-email = ["email-validator (>=2.0.0)"]
-timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows\""]
-
-[[package]]
-name = "pydantic-core"
-version = "2.33.2"
-description = "Core functionality for Pydantic validation and serialization"
-optional = false
-python-versions = ">=3.9"
-groups = ["main", "dev"]
-files = [
- {file = "pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8"},
- {file = "pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d"},
- {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d"},
- {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572"},
- {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02"},
- {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b"},
- {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2"},
- {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a"},
- {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac"},
- {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a"},
- {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b"},
- {file = "pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22"},
- {file = "pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640"},
- {file = "pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7"},
- {file = "pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246"},
- {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f"},
- {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc"},
- {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de"},
- {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a"},
- {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef"},
- {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e"},
- {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d"},
- {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30"},
- {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf"},
- {file = "pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51"},
- {file = "pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab"},
- {file = "pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65"},
- {file = "pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc"},
- {file = "pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7"},
- {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025"},
- {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011"},
- {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f"},
- {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88"},
- {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1"},
- {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b"},
- {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1"},
- {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6"},
- {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea"},
- {file = "pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290"},
- {file = "pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2"},
- {file = "pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab"},
- {file = "pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f"},
- {file = "pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6"},
- {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef"},
- {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a"},
- {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916"},
- {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a"},
- {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d"},
- {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56"},
- {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5"},
- {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e"},
- {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162"},
- {file = "pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849"},
- {file = "pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9"},
- {file = "pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9"},
- {file = "pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac"},
- {file = "pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5"},
- {file = "pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9"},
- {file = "pydantic_core-2.33.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a2b911a5b90e0374d03813674bf0a5fbbb7741570dcd4b4e85a2e48d17def29d"},
- {file = "pydantic_core-2.33.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6fa6dfc3e4d1f734a34710f391ae822e0a8eb8559a85c6979e14e65ee6ba2954"},
- {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c54c939ee22dc8e2d545da79fc5381f1c020d6d3141d3bd747eab59164dc89fb"},
- {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53a57d2ed685940a504248187d5685e49eb5eef0f696853647bf37c418c538f7"},
- {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09fb9dd6571aacd023fe6aaca316bd01cf60ab27240d7eb39ebd66a3a15293b4"},
- {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e6116757f7959a712db11f3e9c0a99ade00a5bbedae83cb801985aa154f071b"},
- {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d55ab81c57b8ff8548c3e4947f119551253f4e3787a7bbc0b6b3ca47498a9d3"},
- {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c20c462aa4434b33a2661701b861604913f912254e441ab8d78d30485736115a"},
- {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:44857c3227d3fb5e753d5fe4a3420d6376fa594b07b621e220cd93703fe21782"},
- {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:eb9b459ca4df0e5c87deb59d37377461a538852765293f9e6ee834f0435a93b9"},
- {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9fcd347d2cc5c23b06de6d3b7b8275be558a0c90549495c699e379a80bf8379e"},
- {file = "pydantic_core-2.33.2-cp39-cp39-win32.whl", hash = "sha256:83aa99b1285bc8f038941ddf598501a86f1536789740991d7d8756e34f1e74d9"},
- {file = "pydantic_core-2.33.2-cp39-cp39-win_amd64.whl", hash = "sha256:f481959862f57f29601ccced557cc2e817bce7533ab8e01a797a48b49c9692b3"},
- {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa"},
- {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29"},
- {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d"},
- {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e"},
- {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c"},
- {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec"},
- {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052"},
- {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c"},
- {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808"},
- {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8"},
- {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593"},
- {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612"},
- {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7"},
- {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e"},
- {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8"},
- {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf"},
- {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb"},
- {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1"},
- {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:87acbfcf8e90ca885206e98359d7dca4bcbb35abdc0ff66672a293e1d7a19101"},
- {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7f92c15cd1e97d4b12acd1cc9004fa092578acfa57b67ad5e43a197175d01a64"},
- {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3f26877a748dc4251cfcfda9dfb5f13fcb034f5308388066bcfe9031b63ae7d"},
- {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac89aea9af8cd672fa7b510e7b8c33b0bba9a43186680550ccf23020f32d535"},
- {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:970919794d126ba8645f3837ab6046fb4e72bbc057b3709144066204c19a455d"},
- {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3eb3fe62804e8f859c49ed20a8451342de53ed764150cb14ca71357c765dc2a6"},
- {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:3abcd9392a36025e3bd55f9bd38d908bd17962cc49bc6da8e7e96285336e2bca"},
- {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:3a1c81334778f9e3af2f8aeb7a960736e5cab1dfebfb26aabca09afd2906c039"},
- {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2807668ba86cb38c6817ad9bc66215ab8584d1d304030ce4f0887336f28a5e27"},
- {file = "pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc"},
-]
-
-[package.dependencies]
-typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
-
-[[package]]
-name = "pydantic-settings"
-version = "2.10.1"
-description = "Settings management using Pydantic"
-optional = false
-python-versions = ">=3.9"
-groups = ["main", "dev"]
-files = [
- {file = "pydantic_settings-2.10.1-py3-none-any.whl", hash = "sha256:a60952460b99cf661dc25c29c0ef171721f98bfcb52ef8d9ea4c943d7c8cc796"},
- {file = "pydantic_settings-2.10.1.tar.gz", hash = "sha256:06f0062169818d0f5524420a360d632d5857b83cffd4d42fe29597807a1614ee"},
-]
-
-[package.dependencies]
-pydantic = ">=2.7.0"
-python-dotenv = ">=0.21.0"
-typing-inspection = ">=0.4.0"
-
-[package.extras]
-aws-secrets-manager = ["boto3 (>=1.35.0)", "boto3-stubs[secretsmanager]"]
-azure-key-vault = ["azure-identity (>=1.16.0)", "azure-keyvault-secrets (>=4.8.0)"]
-gcp-secret-manager = ["google-cloud-secret-manager (>=2.23.1)"]
-toml = ["tomli (>=2.0.1)"]
-yaml = ["pyyaml (>=6.0.1)"]
-
-[[package]]
-name = "pydeck"
-version = "0.9.1"
-description = "Widget for deck.gl maps"
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "pydeck-0.9.1-py2.py3-none-any.whl", hash = "sha256:b3f75ba0d273fc917094fa61224f3f6076ca8752b93d46faf3bcfd9f9d59b038"},
- {file = "pydeck-0.9.1.tar.gz", hash = "sha256:f74475ae637951d63f2ee58326757f8d4f9cd9f2a457cf42950715003e2cb605"},
-]
-
-[package.dependencies]
-jinja2 = ">=2.10.1"
-numpy = ">=1.16.4"
-
-[package.extras]
-carto = ["pydeck-carto"]
-jupyter = ["ipykernel (>=5.1.2) ; python_version >= \"3.4\"", "ipython (>=5.8.0) ; python_version < \"3.4\"", "ipywidgets (>=7,<8)", "traitlets (>=4.3.2)"]
-
-[[package]]
-name = "pygments"
-version = "2.19.2"
-description = "Pygments is a syntax highlighting package written in Python."
-optional = false
-python-versions = ">=3.8"
-groups = ["dev", "xla"]
-files = [
- {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"},
- {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"},
-]
-
-[package.extras]
-windows-terminal = ["colorama (>=0.4.6)"]
-
-[[package]]
-name = "pymupdf"
-version = "1.26.4"
-description = "A high performance Python library for data extraction, analysis, conversion & manipulation of PDF (and other) documents."
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "pymupdf-1.26.4-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cb95562a0a63ce906fd788bdad5239063b63068cf4a991684f43acb09052cb99"},
- {file = "pymupdf-1.26.4-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:67e9e6b45832c33726651c2a031e9a20108fd9e759140b9e843f934de813a7ff"},
- {file = "pymupdf-1.26.4-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:2604f687dd02b6a1b98c81bd8becfc0024899a2d2085adfe3f9e91607721fd22"},
- {file = "pymupdf-1.26.4-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:973a6dda61ebd34040e4df3753bf004b669017663fbbfdaa294d44eceba98de0"},
- {file = "pymupdf-1.26.4-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:299a49797df5b558e695647fa791329ba3911cbbb31ed65f24a6266c118ef1a7"},
- {file = "pymupdf-1.26.4-cp39-abi3-win32.whl", hash = "sha256:51b38379aad8c71bd7a8dd24d93fbe7580c2a5d9d7e1f9cd29ebbba315aa1bd1"},
- {file = "pymupdf-1.26.4-cp39-abi3-win_amd64.whl", hash = "sha256:0b6345a93a9afd28de2567e433055e873205c52e6b920b129ca50e836a3aeec6"},
- {file = "pymupdf-1.26.4.tar.gz", hash = "sha256:be13a066d42bfaed343a488168656637c4d9843ddc63b768dc827c9dfc6b9989"},
-]
-
-[[package]]
-name = "pypdfium2"
-version = "4.30.0"
-description = "Python bindings to PDFium"
-optional = false
-python-versions = ">=3.6"
-groups = ["main", "dev"]
-files = [
- {file = "pypdfium2-4.30.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:b33ceded0b6ff5b2b93bc1fe0ad4b71aa6b7e7bd5875f1ca0cdfb6ba6ac01aab"},
- {file = "pypdfium2-4.30.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:4e55689f4b06e2d2406203e771f78789bd4f190731b5d57383d05cf611d829de"},
- {file = "pypdfium2-4.30.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e6e50f5ce7f65a40a33d7c9edc39f23140c57e37144c2d6d9e9262a2a854854"},
- {file = "pypdfium2-4.30.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3d0dd3ecaffd0b6dbda3da663220e705cb563918249bda26058c6036752ba3a2"},
- {file = "pypdfium2-4.30.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc3bf29b0db8c76cdfaac1ec1cde8edf211a7de7390fbf8934ad2aa9b4d6dfad"},
- {file = "pypdfium2-4.30.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1f78d2189e0ddf9ac2b7a9b9bd4f0c66f54d1389ff6c17e9fd9dc034d06eb3f"},
- {file = "pypdfium2-4.30.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:5eda3641a2da7a7a0b2f4dbd71d706401a656fea521b6b6faa0675b15d31a163"},
- {file = "pypdfium2-4.30.0-py3-none-musllinux_1_1_i686.whl", hash = "sha256:0dfa61421b5eb68e1188b0b2231e7ba35735aef2d867d86e48ee6cab6975195e"},
- {file = "pypdfium2-4.30.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:f33bd79e7a09d5f7acca3b0b69ff6c8a488869a7fab48fdf400fec6e20b9c8be"},
- {file = "pypdfium2-4.30.0-py3-none-win32.whl", hash = "sha256:ee2410f15d576d976c2ab2558c93d392a25fb9f6635e8dd0a8a3a5241b275e0e"},
- {file = "pypdfium2-4.30.0-py3-none-win_amd64.whl", hash = "sha256:90dbb2ac07be53219f56be09961eb95cf2473f834d01a42d901d13ccfad64b4c"},
- {file = "pypdfium2-4.30.0-py3-none-win_arm64.whl", hash = "sha256:119b2969a6d6b1e8d55e99caaf05290294f2d0fe49c12a3f17102d01c441bd29"},
- {file = "pypdfium2-4.30.0.tar.gz", hash = "sha256:48b5b7e5566665bc1015b9d69c1ebabe21f6aee468b509531c3c8318eeee2e16"},
-]
-
-[[package]]
-name = "pytesseract"
-version = "0.3.13"
-description = "Python-tesseract is a python wrapper for Google's Tesseract-OCR"
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "pytesseract-0.3.13-py3-none-any.whl", hash = "sha256:7a99c6c2ac598360693d83a416e36e0b33a67638bb9d77fdcac094a3589d4b34"},
- {file = "pytesseract-0.3.13.tar.gz", hash = "sha256:4bf5f880c99406f52a3cfc2633e42d9dc67615e69d8a509d74867d3baddb5db9"},
-]
-
-[package.dependencies]
-packaging = ">=21.3"
-Pillow = ">=8.0.0"
-
-[[package]]
-name = "pytest"
-version = "8.4.2"
-description = "pytest: simple powerful testing with Python"
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79"},
- {file = "pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01"},
-]
-
-[package.dependencies]
-colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""}
-exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""}
-iniconfig = ">=1"
-packaging = ">=20"
-pluggy = ">=1.5,<2"
-pygments = ">=2.7.2"
-tomli = {version = ">=1", markers = "python_version < \"3.11\""}
-
-[package.extras]
-dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"]
-
-[[package]]
-name = "python-dateutil"
-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 = ["dev"]
-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"},
-]
-
-[package.dependencies]
-six = ">=1.5"
-
-[[package]]
-name = "python-dotenv"
-version = "1.1.1"
-description = "Read key-value pairs from a .env file and set them as environment variables"
-optional = false
-python-versions = ">=3.9"
-groups = ["main", "dev"]
-files = [
- {file = "python_dotenv-1.1.1-py3-none-any.whl", hash = "sha256:31f23644fe2602f88ff55e1f5c79ba497e01224ee7737937930c448e4d0e24dc"},
- {file = "python_dotenv-1.1.1.tar.gz", hash = "sha256:a8a6399716257f45be6a007360200409fce5cda2661e3dec71d23dc15f6189ab"},
-]
-
-[package.extras]
-cli = ["click (>=5.0)"]
-
-[[package]]
-name = "python-json-logger"
-version = "3.3.0"
-description = "JSON Log Formatter for the Python Logging Package"
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "python_json_logger-3.3.0-py3-none-any.whl", hash = "sha256:dd980fae8cffb24c13caf6e158d3d61c0d6d22342f932cb6e9deedab3d35eec7"},
- {file = "python_json_logger-3.3.0.tar.gz", hash = "sha256:12b7e74b17775e7d565129296105bbe3910842d9d0eb083fc83a6a617aa8df84"},
-]
-
-[package.extras]
-dev = ["backports.zoneinfo ; python_version < \"3.9\"", "black", "build", "freezegun", "mdx_truly_sane_lists", "mike", "mkdocs", "mkdocs-awesome-pages-plugin", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-material (>=8.5)", "mkdocstrings[python]", "msgspec ; implementation_name != \"pypy\"", "mypy", "orjson ; implementation_name != \"pypy\"", "pylint", "pytest", "tzdata", "validate-pyproject[all]"]
-
-[[package]]
-name = "pytz"
-version = "2025.2"
-description = "World timezone definitions, modern and historical"
-optional = false
-python-versions = "*"
-groups = ["dev"]
-files = [
- {file = "pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00"},
- {file = "pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3"},
-]
-
-[[package]]
-name = "pywin32"
-version = "311"
-description = "Python for Window Extensions"
-optional = false
-python-versions = "*"
-groups = ["dev"]
-markers = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\""
-files = [
- {file = "pywin32-311-cp310-cp310-win32.whl", hash = "sha256:d03ff496d2a0cd4a5893504789d4a15399133fe82517455e78bad62efbb7f0a3"},
- {file = "pywin32-311-cp310-cp310-win_amd64.whl", hash = "sha256:797c2772017851984b97180b0bebe4b620bb86328e8a884bb626156295a63b3b"},
- {file = "pywin32-311-cp310-cp310-win_arm64.whl", hash = "sha256:0502d1facf1fed4839a9a51ccbcc63d952cf318f78ffc00a7e78528ac27d7a2b"},
- {file = "pywin32-311-cp311-cp311-win32.whl", hash = "sha256:184eb5e436dea364dcd3d2316d577d625c0351bf237c4e9a5fabbcfa5a58b151"},
- {file = "pywin32-311-cp311-cp311-win_amd64.whl", hash = "sha256:3ce80b34b22b17ccbd937a6e78e7225d80c52f5ab9940fe0506a1a16f3dab503"},
- {file = "pywin32-311-cp311-cp311-win_arm64.whl", hash = "sha256:a733f1388e1a842abb67ffa8e7aad0e70ac519e09b0f6a784e65a136ec7cefd2"},
- {file = "pywin32-311-cp312-cp312-win32.whl", hash = "sha256:750ec6e621af2b948540032557b10a2d43b0cee2ae9758c54154d711cc852d31"},
- {file = "pywin32-311-cp312-cp312-win_amd64.whl", hash = "sha256:b8c095edad5c211ff31c05223658e71bf7116daa0ecf3ad85f3201ea3190d067"},
- {file = "pywin32-311-cp312-cp312-win_arm64.whl", hash = "sha256:e286f46a9a39c4a18b319c28f59b61de793654af2f395c102b4f819e584b5852"},
- {file = "pywin32-311-cp313-cp313-win32.whl", hash = "sha256:f95ba5a847cba10dd8c4d8fefa9f2a6cf283b8b88ed6178fa8a6c1ab16054d0d"},
- {file = "pywin32-311-cp313-cp313-win_amd64.whl", hash = "sha256:718a38f7e5b058e76aee1c56ddd06908116d35147e133427e59a3983f703a20d"},
- {file = "pywin32-311-cp313-cp313-win_arm64.whl", hash = "sha256:7b4075d959648406202d92a2310cb990fea19b535c7f4a78d3f5e10b926eeb8a"},
- {file = "pywin32-311-cp314-cp314-win32.whl", hash = "sha256:b7a2c10b93f8986666d0c803ee19b5990885872a7de910fc460f9b0c2fbf92ee"},
- {file = "pywin32-311-cp314-cp314-win_amd64.whl", hash = "sha256:3aca44c046bd2ed8c90de9cb8427f581c479e594e99b5c0bb19b29c10fd6cb87"},
- {file = "pywin32-311-cp314-cp314-win_arm64.whl", hash = "sha256:a508e2d9025764a8270f93111a970e1d0fbfc33f4153b388bb649b7eec4f9b42"},
- {file = "pywin32-311-cp38-cp38-win32.whl", hash = "sha256:6c6f2969607b5023b0d9ce2541f8d2cbb01c4f46bc87456017cf63b73f1e2d8c"},
- {file = "pywin32-311-cp38-cp38-win_amd64.whl", hash = "sha256:c8015b09fb9a5e188f83b7b04de91ddca4658cee2ae6f3bc483f0b21a77ef6cd"},
- {file = "pywin32-311-cp39-cp39-win32.whl", hash = "sha256:aba8f82d551a942cb20d4a83413ccbac30790b50efb89a75e4f586ac0bb8056b"},
- {file = "pywin32-311-cp39-cp39-win_amd64.whl", hash = "sha256:e0c4cfb0621281fe40387df582097fd796e80430597cb9944f0ae70447bacd91"},
- {file = "pywin32-311-cp39-cp39-win_arm64.whl", hash = "sha256:62ea666235135fee79bb154e695f3ff67370afefd71bd7fea7512fc70ef31e3d"},
-]
-
-[[package]]
-name = "pywinpty"
-version = "3.0.0"
-description = ""
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-markers = "os_name == \"nt\""
-files = [
- {file = "pywinpty-3.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:327b6034e0dc38352c1c99a7c0b3e54941b4e506a5f21acce63609cd2ab6cce2"},
- {file = "pywinpty-3.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:29daa71ac5dcbe1496ef99f4cde85a732b1f0a3b71405d42177dbcf9ee405e5a"},
- {file = "pywinpty-3.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:1e0c4b01e5b03b1531d7c5d0e044b8c66dd0288c6d2b661820849f2a8d91aec3"},
- {file = "pywinpty-3.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:828cbe756b7e3d25d886fbd5691a1d523cd59c5fb79286bb32bb75c5221e7ba1"},
- {file = "pywinpty-3.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:de0cbe27b96e5a2cebd86c4a6b8b4139f978d9c169d44a8edc7e30e88e5d7a69"},
- {file = "pywinpty-3.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:007735316170ec1b6e773deadab5fe9ec4074dfdc06f27513fe87b8cfe45237d"},
- {file = "pywinpty-3.0.0.tar.gz", hash = "sha256:68f70e68a9f0766ffdea3fc500351cb7b9b012bcb8239a411f7ff0fc8f86dcb1"},
-]
-
-[[package]]
-name = "pyyaml"
-version = "6.0.2"
-description = "YAML parser and emitter for Python"
-optional = false
-python-versions = ">=3.8"
-groups = ["main", "dev", "xla"]
-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"},
- {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"},
- {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"},
- {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"},
- {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"},
- {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"},
- {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"},
- {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"},
- {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"},
- {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"},
- {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"},
- {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"},
- {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"},
- {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"},
- {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"},
- {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"},
- {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"},
- {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"},
- {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"},
- {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"},
- {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"},
- {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"},
- {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"},
- {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"},
- {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"},
- {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"},
- {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"},
- {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"},
- {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"},
- {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"},
- {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"},
- {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"},
- {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"},
- {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"},
- {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"},
- {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"},
- {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"},
- {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"},
- {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"},
- {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"},
- {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"},
- {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"},
- {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"},
- {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"},
- {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"},
- {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"},
- {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"},
- {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"},
- {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"},
- {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"},
- {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"},
- {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"},
-]
-
-[[package]]
-name = "pyzmq"
-version = "27.0.2"
-description = "Python bindings for 0MQ"
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "pyzmq-27.0.2-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:8b32c4636ced87dce0ac3d671e578b3400215efab372f1b4be242e8cf0b11384"},
- {file = "pyzmq-27.0.2-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:f9528a4b3e24189cb333a9850fddbbafaa81df187297cfbddee50447cdb042cf"},
- {file = "pyzmq-27.0.2-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3b02ba0c0b2b9ebe74688002e6c56c903429924a25630804b9ede1f178aa5a3f"},
- {file = "pyzmq-27.0.2-cp310-cp310-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e4dc5c9a6167617251dea0d024d67559795761aabb4b7ea015518be898be076"},
- {file = "pyzmq-27.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f1151b33aaf3b4fa9da26f4d696e38eebab67d1b43c446184d733c700b3ff8ce"},
- {file = "pyzmq-27.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:4ecfc7999ac44c9ef92b5ae8f0b44fb935297977df54d8756b195a3cd12f38f0"},
- {file = "pyzmq-27.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:31c26a5d0b00befcaeeb600d8b15ad09f5604b6f44e2057ec5e521a9e18dcd9a"},
- {file = "pyzmq-27.0.2-cp310-cp310-win32.whl", hash = "sha256:25a100d2de2ac0c644ecf4ce0b509a720d12e559c77aff7e7e73aa684f0375bc"},
- {file = "pyzmq-27.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a1acf091f53bb406e9e5e7383e467d1dd1b94488b8415b890917d30111a1fef3"},
- {file = "pyzmq-27.0.2-cp310-cp310-win_arm64.whl", hash = "sha256:b38e01f11e9e95f6668dc8a62dccf9483f454fed78a77447507a0e8dcbd19a63"},
- {file = "pyzmq-27.0.2-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:063845960df76599ad4fad69fa4d884b3ba38304272104fdcd7e3af33faeeb1d"},
- {file = "pyzmq-27.0.2-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:845a35fb21b88786aeb38af8b271d41ab0967985410f35411a27eebdc578a076"},
- {file = "pyzmq-27.0.2-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:515d20b5c3c86db95503faa989853a8ab692aab1e5336db011cd6d35626c4cb1"},
- {file = "pyzmq-27.0.2-cp311-cp311-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:862aedec0b0684a5050cdb5ec13c2da96d2f8dffda48657ed35e312a4e31553b"},
- {file = "pyzmq-27.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2cb5bcfc51c7a4fce335d3bc974fd1d6a916abbcdd2b25f6e89d37b8def25f57"},
- {file = "pyzmq-27.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:38ff75b2a36e3a032e9fef29a5871e3e1301a37464e09ba364e3c3193f62982a"},
- {file = "pyzmq-27.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7a5709abe8d23ca158a9d0a18c037f4193f5b6afeb53be37173a41e9fb885792"},
- {file = "pyzmq-27.0.2-cp311-cp311-win32.whl", hash = "sha256:47c5dda2018c35d87be9b83de0890cb92ac0791fd59498847fc4eca6ff56671d"},
- {file = "pyzmq-27.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:f54ca3e98f8f4d23e989c7d0edcf9da7a514ff261edaf64d1d8653dd5feb0a8b"},
- {file = "pyzmq-27.0.2-cp311-cp311-win_arm64.whl", hash = "sha256:2ef3067cb5b51b090fb853f423ad7ed63836ec154374282780a62eb866bf5768"},
- {file = "pyzmq-27.0.2-cp312-abi3-macosx_10_15_universal2.whl", hash = "sha256:5da05e3c22c95e23bfc4afeee6ff7d4be9ff2233ad6cb171a0e8257cd46b169a"},
- {file = "pyzmq-27.0.2-cp312-abi3-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4e4520577971d01d47e2559bb3175fce1be9103b18621bf0b241abe0a933d040"},
- {file = "pyzmq-27.0.2-cp312-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:56d7de7bf73165b90bd25a8668659ccb134dd28449116bf3c7e9bab5cf8a8ec9"},
- {file = "pyzmq-27.0.2-cp312-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:340e7cddc32f147c6c00d116a3f284ab07ee63dbd26c52be13b590520434533c"},
- {file = "pyzmq-27.0.2-cp312-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ba95693f9df8bb4a9826464fb0fe89033936f35fd4a8ff1edff09a473570afa0"},
- {file = "pyzmq-27.0.2-cp312-abi3-musllinux_1_2_i686.whl", hash = "sha256:ca42a6ce2d697537da34f77a1960d21476c6a4af3e539eddb2b114c3cf65a78c"},
- {file = "pyzmq-27.0.2-cp312-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3e44e665d78a07214b2772ccbd4b9bcc6d848d7895f1b2d7653f047b6318a4f6"},
- {file = "pyzmq-27.0.2-cp312-abi3-win32.whl", hash = "sha256:272d772d116615397d2be2b1417b3b8c8bc8671f93728c2f2c25002a4530e8f6"},
- {file = "pyzmq-27.0.2-cp312-abi3-win_amd64.whl", hash = "sha256:734be4f44efba0aa69bf5f015ed13eb69ff29bf0d17ea1e21588b095a3147b8e"},
- {file = "pyzmq-27.0.2-cp312-abi3-win_arm64.whl", hash = "sha256:41f0bd56d9279392810950feb2785a419c2920bbf007fdaaa7f4a07332ae492d"},
- {file = "pyzmq-27.0.2-cp313-cp313-android_24_arm64_v8a.whl", hash = "sha256:7f01118133427cd7f34ee133b5098e2af5f70303fa7519785c007bca5aa6f96a"},
- {file = "pyzmq-27.0.2-cp313-cp313-android_24_x86_64.whl", hash = "sha256:e4b860edf6379a7234ccbb19b4ed2c57e3ff569c3414fadfb49ae72b61a8ef07"},
- {file = "pyzmq-27.0.2-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:cb77923ea163156da14295c941930bd525df0d29c96c1ec2fe3c3806b1e17cb3"},
- {file = "pyzmq-27.0.2-cp313-cp313t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:61678b7407b04df8f9423f188156355dc94d0fb52d360ae79d02ed7e0d431eea"},
- {file = "pyzmq-27.0.2-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e3c824b70925963bdc8e39a642672c15ffaa67e7d4b491f64662dd56d6271263"},
- {file = "pyzmq-27.0.2-cp313-cp313t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c4833e02fcf2751975457be1dfa2f744d4d09901a8cc106acaa519d868232175"},
- {file = "pyzmq-27.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:b18045668d09cf0faa44918af2a67f0dbbef738c96f61c2f1b975b1ddb92ccfc"},
- {file = "pyzmq-27.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:bbbb7e2f3ac5a22901324e7b086f398b8e16d343879a77b15ca3312e8cd8e6d5"},
- {file = "pyzmq-27.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:b751914a73604d40d88a061bab042a11d4511b3ddbb7624cd83c39c8a498564c"},
- {file = "pyzmq-27.0.2-cp313-cp313t-win32.whl", hash = "sha256:3e8f833dd82af11db5321c414638045c70f61009f72dd61c88db4a713c1fb1d2"},
- {file = "pyzmq-27.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:5b45153cb8eadcab14139970643a84f7a7b08dda541fbc1f6f4855c49334b549"},
- {file = "pyzmq-27.0.2-cp313-cp313t-win_arm64.whl", hash = "sha256:86898f5c9730df23427c1ee0097d8aa41aa5f89539a79e48cd0d2c22d059f1b7"},
- {file = "pyzmq-27.0.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:d2b4b261dce10762be5c116b6ad1f267a9429765b493c454f049f33791dd8b8a"},
- {file = "pyzmq-27.0.2-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:4e4d88b6cff156fed468903006b24bbd85322612f9c2f7b96e72d5016fd3f543"},
- {file = "pyzmq-27.0.2-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8426c0ebbc11ed8416a6e9409c194142d677c2c5c688595f2743664e356d9e9b"},
- {file = "pyzmq-27.0.2-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:565bee96a155fe6452caed5fb5f60c9862038e6b51a59f4f632562081cdb4004"},
- {file = "pyzmq-27.0.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5de735c745ca5cefe9c2d1547d8f28cfe1b1926aecb7483ab1102fd0a746c093"},
- {file = "pyzmq-27.0.2-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ea4f498f8115fd90d7bf03a3e83ae3e9898e43362f8e8e8faec93597206e15cc"},
- {file = "pyzmq-27.0.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d00e81cb0afd672915257a3927124ee2ad117ace3c256d39cd97ca3f190152ad"},
- {file = "pyzmq-27.0.2-cp314-cp314t-win32.whl", hash = "sha256:0f6e9b00d81b58f859fffc112365d50413954e02aefe36c5b4c8fb4af79f8cc3"},
- {file = "pyzmq-27.0.2-cp314-cp314t-win_amd64.whl", hash = "sha256:2e73cf3b127a437fef4100eb3ac2ebe6b49e655bb721329f667f59eca0a26221"},
- {file = "pyzmq-27.0.2-cp314-cp314t-win_arm64.whl", hash = "sha256:4108785f2e5ac865d06f678a07a1901e3465611356df21a545eeea8b45f56265"},
- {file = "pyzmq-27.0.2-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:59a50f5eedf8ed20b7dbd57f1c29b2de003940dea3eedfbf0fbfea05ee7f9f61"},
- {file = "pyzmq-27.0.2-cp38-cp38-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:a00e6390e52770ba1ec753b2610f90b4f00e74c71cfc5405b917adf3cc39565e"},
- {file = "pyzmq-27.0.2-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:49d8d05d9844d83cddfbc86a82ac0cafe7ab694fcc9c9618de8d015c318347c3"},
- {file = "pyzmq-27.0.2-cp38-cp38-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3660d85e2b6a28eb2d586dedab9c61a7b7c64ab0d89a35d2973c7be336f12b0d"},
- {file = "pyzmq-27.0.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:bccfee44b392f4d13bbf05aa88d8f7709271b940a8c398d4216fde6b717624ae"},
- {file = "pyzmq-27.0.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:989066d51686415f1da646d6e2c5364a9b084777c29d9d1720aa5baf192366ef"},
- {file = "pyzmq-27.0.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:cc283595b82f0db155a52f6462945c7b6b47ecaae2f681746eeea537c95cf8c9"},
- {file = "pyzmq-27.0.2-cp38-cp38-win32.whl", hash = "sha256:ad38daf57495beadc0d929e8901b2aa46ff474239b5a8a46ccc7f67dc01d2335"},
- {file = "pyzmq-27.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:36508466a266cf78bba2f56529ad06eb38ba827f443b47388d420bec14d331ba"},
- {file = "pyzmq-27.0.2-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:aa9c1c208c263b84386ac25bed6af5672397dc3c232638114fc09bca5c7addf9"},
- {file = "pyzmq-27.0.2-cp39-cp39-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:795c4884cfe7ea59f2b67d82b417e899afab889d332bfda13b02f8e0c155b2e4"},
- {file = "pyzmq-27.0.2-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:47eb65bb25478358ba3113dd9a08344f616f417ad3ffcbb190cd874fae72b1b1"},
- {file = "pyzmq-27.0.2-cp39-cp39-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6fc24f00293f10aff04d55ca37029b280474c91f4de2cad5e911e5e10d733b7"},
- {file = "pyzmq-27.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:58d4cc9b6b768478adfc40a5cbee545303db8dbc81ba688474e0f499cc581028"},
- {file = "pyzmq-27.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:cea2f26c5972796e02b222968a21a378d09eb4ff590eb3c5fafa8913f8c2bdf5"},
- {file = "pyzmq-27.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a0621ec020c49fc1b6e31304f1a820900d54e7d9afa03ea1634264bf9387519e"},
- {file = "pyzmq-27.0.2-cp39-cp39-win32.whl", hash = "sha256:1326500792a9cb0992db06bbaf5d0098459133868932b81a6e90d45c39eca99d"},
- {file = "pyzmq-27.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:5ee9560cb1e3094ef01fc071b361121a57ebb8d4232912b6607a6d7d2d0a97b4"},
- {file = "pyzmq-27.0.2-cp39-cp39-win_arm64.whl", hash = "sha256:85e3c6fb0d25ea046ebcfdc2bcb9683d663dc0280645c79a616ff5077962a15b"},
- {file = "pyzmq-27.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:d67a0960803a37b60f51b460c58444bc7033a804c662f5735172e21e74ee4902"},
- {file = "pyzmq-27.0.2-pp310-pypy310_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:dd4d3e6a567ffd0d232cfc667c49d0852d0ee7481458a2a1593b9b1bc5acba88"},
- {file = "pyzmq-27.0.2-pp310-pypy310_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5e558be423631704803bc6a642e2caa96083df759e25fe6eb01f2d28725f80bd"},
- {file = "pyzmq-27.0.2-pp310-pypy310_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c4c20ba8389f495c7b4f6b896bb1ca1e109a157d4f189267a902079699aaf787"},
- {file = "pyzmq-27.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c5be232f7219414ff672ff7ab8c5a7e8632177735186d8a42b57b491fafdd64e"},
- {file = "pyzmq-27.0.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e297784aea724294fe95e442e39a4376c2f08aa4fae4161c669f047051e31b02"},
- {file = "pyzmq-27.0.2-pp311-pypy311_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e3659a79ded9745bc9c2aef5b444ac8805606e7bc50d2d2eb16dc3ab5483d91f"},
- {file = "pyzmq-27.0.2-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3dba49ff037d02373a9306b58d6c1e0be031438f822044e8767afccfdac4c6b"},
- {file = "pyzmq-27.0.2-pp311-pypy311_pp73-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de84e1694f9507b29e7b263453a2255a73e3d099d258db0f14539bad258abe41"},
- {file = "pyzmq-27.0.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f0944d65ba2b872b9fcece08411d6347f15a874c775b4c3baae7f278550da0fb"},
- {file = "pyzmq-27.0.2-pp38-pypy38_pp73-macosx_10_15_x86_64.whl", hash = "sha256:05288947797dcd6724702db2056972dceef9963a83041eb734aea504416094ec"},
- {file = "pyzmq-27.0.2-pp38-pypy38_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:dff9198adbb6810ad857f3bfa59b4859c45acb02b0d198b39abeafb9148474f3"},
- {file = "pyzmq-27.0.2-pp38-pypy38_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:849123fd9982c7f63911fdceba9870f203f0f32c953a3bab48e7f27803a0e3ec"},
- {file = "pyzmq-27.0.2-pp38-pypy38_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c5ee06945f3069e3609819890a01958c4bbfea7a2b31ae87107c6478838d309e"},
- {file = "pyzmq-27.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:6156ad5e8bbe8a78a3f5b5757c9a883b0012325c83f98ce6d58fcec81e8b3d06"},
- {file = "pyzmq-27.0.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:400f34321e3bd89b1165b91ea6b18ad26042ba9ad0dfed8b35049e2e24eeab9b"},
- {file = "pyzmq-27.0.2-pp39-pypy39_pp73-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:9cbad4ef12e4c15c94d2c24ecd15a8ed56bf091c62f121a2b0c618ddd4b7402b"},
- {file = "pyzmq-27.0.2-pp39-pypy39_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6b2b74aac3392b8cf508ccb68c980a8555298cd378434a2d065d6ce0f4211dff"},
- {file = "pyzmq-27.0.2-pp39-pypy39_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7db5db88c24cf9253065d69229a148ff60821e5d6f8ff72579b1f80f8f348bab"},
- {file = "pyzmq-27.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:8ffe40c216c41756ca05188c3e24a23142334b304f7aebd75c24210385e35573"},
- {file = "pyzmq-27.0.2.tar.gz", hash = "sha256:b398dd713b18de89730447347e96a0240225e154db56e35b6bb8447ffdb07798"},
-]
-
-[package.dependencies]
-cffi = {version = "*", markers = "implementation_name == \"pypy\""}
-
-[[package]]
-name = "rapidfuzz"
-version = "3.14.0"
-description = "rapid fuzzy string matching"
-optional = false
-python-versions = ">=3.10"
-groups = ["dev"]
-files = [
- {file = "rapidfuzz-3.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:91d8c7d9d38835d5fcf9bc87593add864eaea41eb33654d93ded3006b198a326"},
- {file = "rapidfuzz-3.14.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5a1e574230262956d28e40191dd44ad3d81d2d29b5e716c6c7c0ba17c4d1524e"},
- {file = "rapidfuzz-3.14.0-cp310-cp310-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f1eda6546831f15e6d8d27593873129ae5e4d2f05cf13bacc2d5222e117f3038"},
- {file = "rapidfuzz-3.14.0-cp310-cp310-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d29686b524b35f93fc14961026a8cfb37283af76ab6f4ed49aebf4df01b44a4a"},
- {file = "rapidfuzz-3.14.0-cp310-cp310-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0fb99bc445014e893c152e36e98b3e9418cc2c0fa7b83d01f3d1b89e73618ed2"},
- {file = "rapidfuzz-3.14.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0d9cd4212ca2ea18d026b3f3dfc1ec25919e75ddfd2c7dd20bf7797f262e2460"},
- {file = "rapidfuzz-3.14.0-cp310-cp310-manylinux_2_31_armv7l.whl", hash = "sha256:e6a41c6be1394b17b03bc3af3051f54ba0b4018324a0d4cb34c7d2344ec82e79"},
- {file = "rapidfuzz-3.14.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:19bee793c4a84b0f5153fcff2e7cfeaeeb976497a5892baaadb6eadef7e6f398"},
- {file = "rapidfuzz-3.14.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:977144b50b2f1864c825796ad2d41f47a3fd5b7632a2e9905c4d2c8883a8234d"},
- {file = "rapidfuzz-3.14.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ca7c7274bec8085f7a2b68b0490d270a260385d45280d8a2a8ae5884cfb217ba"},
- {file = "rapidfuzz-3.14.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:efa7eca15825c78dc2b9e9e5824fa095cef8954de98e5a6d2f4ad2416a3d5ddf"},
- {file = "rapidfuzz-3.14.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a780c08c41e7ec4336d7a8fcdcd7920df74de6c57be87b72adad4e1b40a31632"},
- {file = "rapidfuzz-3.14.0-cp310-cp310-win32.whl", hash = "sha256:cf540e48175c0620639aa4f4e2b56d61291935c0f684469e8e125e7fa4daef65"},
- {file = "rapidfuzz-3.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:e7769fbc78aba051f514d8a08374e3989124b2d1eee6888c72706a174d0e8a6d"},
- {file = "rapidfuzz-3.14.0-cp310-cp310-win_arm64.whl", hash = "sha256:71442f5e9fad60a4942df3be340acd5315e59aefc5a83534b6a9aa62db67809d"},
- {file = "rapidfuzz-3.14.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6501e49395ad5cecf1623cb4801639faa1c833dbacc07c26fa7b8f7fa19fd1c0"},
- {file = "rapidfuzz-3.14.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c3cd9b8d5e159c67d242f80cae1b9d9b1502779fc69fcd268a1eb7053f58048"},
- {file = "rapidfuzz-3.14.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a578cadbe61f738685ffa20e56e8346847e40ecb033bdc885373a070cfe4a351"},
- {file = "rapidfuzz-3.14.0-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b5b46340872a1736544b23f3c355f292935311623a0e63a271f284ffdbab05e4"},
- {file = "rapidfuzz-3.14.0-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:238422749da213c3dfe36397b746aeda8579682e93b723a1e77655182198e693"},
- {file = "rapidfuzz-3.14.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:83f3ad0e7ad3cf1138e36be26f4cacb7580ac0132b26528a89e8168a0875afd8"},
- {file = "rapidfuzz-3.14.0-cp311-cp311-manylinux_2_31_armv7l.whl", hash = "sha256:7c34e34fb7e01aeea1e84192cf01daf1d56ccc8a0b34c0833f9799b341c6d539"},
- {file = "rapidfuzz-3.14.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a58bbbbdd2a150c76c6b3af5ac2bbe9afcff26e6b17e1f60b6bd766cc7094fcf"},
- {file = "rapidfuzz-3.14.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:d0e50b4bea57bfcda4afee993eef390fd8f0a64981c971ac4decd9452143892d"},
- {file = "rapidfuzz-3.14.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:357eb9d394bfc742d3528e8bb13afa9baebc7fbe863071975426b47fc21db220"},
- {file = "rapidfuzz-3.14.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:fb960ec526030077658764a309b60e907d86d898f8efbe959845ec2873e514eb"},
- {file = "rapidfuzz-3.14.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6bedb19db81d8d723cc4d914cb079d89ff359364184cc3c3db7cef1fc7819444"},
- {file = "rapidfuzz-3.14.0-cp311-cp311-win32.whl", hash = "sha256:8dba3d6e10a34aa255a6f6922cf249f8d0b9829e6b00854e371d803040044f7f"},
- {file = "rapidfuzz-3.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:ce79e37b23c1cbf1dc557159c8f20f6d71e9d28aef63afcf87bcb58c8add096a"},
- {file = "rapidfuzz-3.14.0-cp311-cp311-win_arm64.whl", hash = "sha256:e140ff4b5d0ea386b998137ddd1335a7bd4201ef987d4cb5a48c3e8c174f8aec"},
- {file = "rapidfuzz-3.14.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:93c8739f7bf7931d690aeb527c27e2a61fd578f076d542ddd37e29fa535546b6"},
- {file = "rapidfuzz-3.14.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7596e95ab03da6cff70f4ec9a5298b2802e8bdd443159d18180b186c80df1416"},
- {file = "rapidfuzz-3.14.0-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cdd49e097ced3746eadb5fb87379f377c0b093f9aba1133ae4f311b574e2ed8"},
- {file = "rapidfuzz-3.14.0-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f4cd4898f21686bb141e151ba920bcd1744cab339277f484c0f97fe7de2c45c8"},
- {file = "rapidfuzz-3.14.0-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:83427518ad72050add47e2cf581080bde81df7f69882e508da3e08faad166b1f"},
- {file = "rapidfuzz-3.14.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05435b4f2472cbf7aac8b837e2e84a165e595c60d79da851da7cfa85ed15895d"},
- {file = "rapidfuzz-3.14.0-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:2dae744c1cdb8b1411ed511a719b505a0348da1970a652bfc735598e68779287"},
- {file = "rapidfuzz-3.14.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9ca05daaca07232037014fc6ce2c2ef0a05c69712f6a5e77da6da5209fb04d7c"},
- {file = "rapidfuzz-3.14.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:2227f4b3742295f380adefef7b6338c30434f8a8e18a11895a1a7c9308b6635d"},
- {file = "rapidfuzz-3.14.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:847ea42b5a6077bc796e1b99cd357a641207b20e3573917b0469b28b5a22238a"},
- {file = "rapidfuzz-3.14.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:539506f13cf0dd6ef2f846571f8e116dba32a468e52d05a91161785ab7de2ed1"},
- {file = "rapidfuzz-3.14.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:03c4b4d4f45f846e4eae052ee18d39d6afe659d74f6d99df5a0d2c5d53930505"},
- {file = "rapidfuzz-3.14.0-cp312-cp312-win32.whl", hash = "sha256:aff0baa3980a8aeb2ce5e15930140146b5fe3fb2d63c8dc4cb08dfbd2051ceb2"},
- {file = "rapidfuzz-3.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:d1eef7f0694fe4cf991f61adaa040955da1e0072c8c41d7db5eb60e83da9e61b"},
- {file = "rapidfuzz-3.14.0-cp312-cp312-win_arm64.whl", hash = "sha256:269d8d1fe5830eef46a165a5c6dd240a05ad44c281a77957461b79cede1ece0f"},
- {file = "rapidfuzz-3.14.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:5cf3828b8cbac02686e1d5c499c58e43c5f613ad936fe19a2d092e53f3308ccd"},
- {file = "rapidfuzz-3.14.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:68c3931c19c51c11654cf75f663f34c0c7ea04c456c84ccebfd52b2047121dba"},
- {file = "rapidfuzz-3.14.0-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9b4232168959af46f2c0770769e7986ff6084d97bc4b6b2b16b2bfa34164421b"},
- {file = "rapidfuzz-3.14.0-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:174c784cecfafe22d783b5124ebffa2e02cc01e49ffe60a28ad86d217977f478"},
- {file = "rapidfuzz-3.14.0-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0b2dedf216f43a50f227eee841ef0480e29e26b2ce2d7ee680b28354ede18627"},
- {file = "rapidfuzz-3.14.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5698239eecf5b759630450ef59521ad3637e5bd4afc2b124ae8af2ff73309c41"},
- {file = "rapidfuzz-3.14.0-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:0acc9553fc26f1c291c381a6aa8d3c5625be23b5721f139528af40cc4119ae1d"},
- {file = "rapidfuzz-3.14.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:00141dfd3b8c9ae15fbb5fbd191a08bde63cdfb1f63095d8f5faf1698e30da93"},
- {file = "rapidfuzz-3.14.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:67f725c3f5713da6e0750dc23f65f0f822c6937c25e3fc9ee797aa6783bef8c1"},
- {file = "rapidfuzz-3.14.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:ba351cf2678d40a23fb4cbfe82cc45ea338a57518dca62a823c5b6381aa20c68"},
- {file = "rapidfuzz-3.14.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:558323dcd5fb38737226be84c78cafbe427706e47379f02c57c3e35ac3745061"},
- {file = "rapidfuzz-3.14.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cb4e4ea174add5183c707d890a816a85e9330f93e5ded139dab182adc727930c"},
- {file = "rapidfuzz-3.14.0-cp313-cp313-win32.whl", hash = "sha256:ec379e1b407935d729c08da9641cfc5dfb2a7796f74cdd82158ce5986bb8ff88"},
- {file = "rapidfuzz-3.14.0-cp313-cp313-win_amd64.whl", hash = "sha256:4b59ba48a909bdf7ec5dad6e3a5a0004aeec141ae5ddb205d0c5bd4389894cf9"},
- {file = "rapidfuzz-3.14.0-cp313-cp313-win_arm64.whl", hash = "sha256:e688b0a98edea42da450fa6ba41736203ead652a78b558839916c10df855f545"},
- {file = "rapidfuzz-3.14.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:cb6c5a46444a2787e466acd77e162049f061304025ab24da02b59caedea66064"},
- {file = "rapidfuzz-3.14.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:99ed7a9e9ff798157caf3c3d96ca7da6560878902d8f70fa7731acc94e0d293c"},
- {file = "rapidfuzz-3.14.0-cp313-cp313t-win32.whl", hash = "sha256:c8e954dd59291ff0cd51b9c0f425e5dc84731bb006dbd5b7846746fe873a0452"},
- {file = "rapidfuzz-3.14.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5754e3ca259667c46a2b58ca7d7568251d6e23d2f0e354ac1cc5564557f4a32d"},
- {file = "rapidfuzz-3.14.0-cp313-cp313t-win_arm64.whl", hash = "sha256:558865f6825d27006e6ae2e1635cfe236d736c8f2c5c82db6db4b1b6df4478bc"},
- {file = "rapidfuzz-3.14.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:3cc4bd8de6643258c5899f21414f9d45d7589d158eee8d438ea069ead624823b"},
- {file = "rapidfuzz-3.14.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:081aac1acb4ab449f8ea7d4e5ea268227295503e1287f56f0b56c7fc3452da1e"},
- {file = "rapidfuzz-3.14.0-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3e0209c6ef7f2c732e10ce4fccafcf7d9e79eb8660a81179aa307c7bd09fafcd"},
- {file = "rapidfuzz-3.14.0-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6e4610997e9de08395e8632b605488a9efc859fe0516b6993b3925f3057f9da7"},
- {file = "rapidfuzz-3.14.0-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efd0095cde6d0179c92c997ede4b85158bf3c7386043e2fadbee291018b29300"},
- {file = "rapidfuzz-3.14.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0a141c07f9e97c45e67aeed677bac92c08f228c556a80750ea3e191e82d54034"},
- {file = "rapidfuzz-3.14.0-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:5a9de40fa6be7809fd2579c8020b9edaf6f50ffc43082b14e95ad3928a254f22"},
- {file = "rapidfuzz-3.14.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20f510dae17bad8f4909ab32b40617f964af55131e630de7ebc0ffa7f00fe634"},
- {file = "rapidfuzz-3.14.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:79c3fd17a432c3f74de94782d7139f9a22e948cec31659a1a05d67b5c0f4290e"},
- {file = "rapidfuzz-3.14.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:8cde9ffb86ea33d67cce9b26b513a177038be48ee2eb4d856cc60a75cb698db7"},
- {file = "rapidfuzz-3.14.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:cafb657c8f2959761bca40c0da66f29d111e2c40d91f8ed4a75cc486c99b33ae"},
- {file = "rapidfuzz-3.14.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4d80a9f673c534800d73f164ed59620e2ba820ed3840abb67c56022ad043564b"},
- {file = "rapidfuzz-3.14.0-cp314-cp314-win32.whl", hash = "sha256:da9878a01357c7906fb16359b3622ce256933a3286058ee503358859e1442f68"},
- {file = "rapidfuzz-3.14.0-cp314-cp314-win_amd64.whl", hash = "sha256:09af941076ef18f6c2b35acfd5004c60d03414414058e98ece6ca9096f454870"},
- {file = "rapidfuzz-3.14.0-cp314-cp314-win_arm64.whl", hash = "sha256:1a878eb065ce6061038dd1c0b9e8eb7477f7d05d5c5161a1d2a5fa630818f938"},
- {file = "rapidfuzz-3.14.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:33ce0326e6feb0d2207a7ca866a5aa6a2ac2361f1ca43ca32aca505268c18ec9"},
- {file = "rapidfuzz-3.14.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e8056d10e99dedf110e929fdff4de6272057115b28eeef4fb6f0d99fd73c026f"},
- {file = "rapidfuzz-3.14.0-cp314-cp314t-win32.whl", hash = "sha256:ddde238b7076e49c2c21a477ee4b67143e1beaf7a3185388fe0b852e64c6ef52"},
- {file = "rapidfuzz-3.14.0-cp314-cp314t-win_amd64.whl", hash = "sha256:ef24464be04a7da1adea741376ddd2b092e0de53c9b500fd3c2e38e071295c9e"},
- {file = "rapidfuzz-3.14.0-cp314-cp314t-win_arm64.whl", hash = "sha256:fd4a27654f51bed3518bc5bbf166627caf3ddd858b12485380685777421f8933"},
- {file = "rapidfuzz-3.14.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4c9a00ef2f684b1132aeb3c0737483dc8f85a725dbe792aee1d1c3cbcf329b34"},
- {file = "rapidfuzz-3.14.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:2e203d76b3dcd1b466ee196f7adb71009860906303db274ae20c7c5af62bc1a8"},
- {file = "rapidfuzz-3.14.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2b317a71fd938348d8dbbe2f559cda58a67fdcafdd3107afca7ab0fb654efa86"},
- {file = "rapidfuzz-3.14.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e5d610a2c5efdb2a3f9eaecac4ecd6d849efb2522efa36000e006179062056dc"},
- {file = "rapidfuzz-3.14.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:c053cad08ab872df4e201daacb66d7fd04b5b4c395baebb193b9910c63ed22ec"},
- {file = "rapidfuzz-3.14.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:7e52ac8a458b2f09291fa968b23192d6664c7568a43607de2a51a088d016152d"},
- {file = "rapidfuzz-3.14.0.tar.gz", hash = "sha256:672b6ba06150e53d7baf4e3d5f12ffe8c213d5088239a15b5ae586ab245ac8b2"},
-]
-
-[package.extras]
-all = ["numpy"]
-
-[[package]]
-name = "referencing"
-version = "0.36.2"
-description = "JSON Referencing + Python"
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0"},
- {file = "referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa"},
-]
-
-[package.dependencies]
-attrs = ">=22.2.0"
-rpds-py = ">=0.7.0"
-typing-extensions = {version = ">=4.4.0", markers = "python_version < \"3.13\""}
-
-[[package]]
-name = "regex"
-version = "2025.9.1"
-description = "Alternative regular expression module, to replace re."
-optional = false
-python-versions = ">=3.9"
-groups = ["main"]
-files = [
- {file = "regex-2025.9.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c5aa2a6a73bf218515484b36a0d20c6ad9dc63f6339ff6224147b0e2c095ee55"},
- {file = "regex-2025.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8c2ff5c01d5e47ad5fc9d31bcd61e78c2fa0068ed00cab86b7320214446da766"},
- {file = "regex-2025.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d49dc84e796b666181de8a9973284cad6616335f01b52bf099643253094920fc"},
- {file = "regex-2025.9.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d9914fe1040874f83c15fcea86d94ea54091b0666eab330aaab69e30d106aabe"},
- {file = "regex-2025.9.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e71bceb3947362ec5eabd2ca0870bb78eae4edfc60c6c21495133c01b6cd2df4"},
- {file = "regex-2025.9.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:67a74456f410fe5e869239ee7a5423510fe5121549af133809d9591a8075893f"},
- {file = "regex-2025.9.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5c3b96ed0223b32dbdc53a83149b6de7ca3acd5acd9c8e64b42a166228abe29c"},
- {file = "regex-2025.9.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:113d5aa950f428faf46fd77d452df62ebb4cc6531cb619f6cc30a369d326bfbd"},
- {file = "regex-2025.9.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:fcdeb38de4f7f3d69d798f4f371189061446792a84e7c92b50054c87aae9c07c"},
- {file = "regex-2025.9.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4bcdff370509164b67a6c8ec23c9fb40797b72a014766fdc159bb809bd74f7d8"},
- {file = "regex-2025.9.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:7383efdf6e8e8c61d85e00cfb2e2e18da1a621b8bfb4b0f1c2747db57b942b8f"},
- {file = "regex-2025.9.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1ec2bd3bdf0f73f7e9f48dca550ba7d973692d5e5e9a90ac42cc5f16c4432d8b"},
- {file = "regex-2025.9.1-cp310-cp310-win32.whl", hash = "sha256:9627e887116c4e9c0986d5c3b4f52bcfe3df09850b704f62ec3cbf177a0ae374"},
- {file = "regex-2025.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:94533e32dc0065eca43912ee6649c90ea0681d59f56d43c45b5bcda9a740b3dd"},
- {file = "regex-2025.9.1-cp310-cp310-win_arm64.whl", hash = "sha256:a874a61bb580d48642ffd338570ee24ab13fa023779190513fcacad104a6e251"},
- {file = "regex-2025.9.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e5bcf112b09bfd3646e4db6bf2e598534a17d502b0c01ea6550ba4eca780c5e6"},
- {file = "regex-2025.9.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:67a0295a3c31d675a9ee0238d20238ff10a9a2fdb7a1323c798fc7029578b15c"},
- {file = "regex-2025.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ea8267fbadc7d4bd7c1301a50e85c2ff0de293ff9452a1a9f8d82c6cafe38179"},
- {file = "regex-2025.9.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6aeff21de7214d15e928fb5ce757f9495214367ba62875100d4c18d293750cc1"},
- {file = "regex-2025.9.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:d89f1bbbbbc0885e1c230f7770d5e98f4f00b0ee85688c871d10df8b184a6323"},
- {file = "regex-2025.9.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ca3affe8ddea498ba9d294ab05f5f2d3b5ad5d515bc0d4a9016dd592a03afe52"},
- {file = "regex-2025.9.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:91892a7a9f0a980e4c2c85dd19bc14de2b219a3a8867c4b5664b9f972dcc0c78"},
- {file = "regex-2025.9.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:e1cb40406f4ae862710615f9f636c1e030fd6e6abe0e0f65f6a695a2721440c6"},
- {file = "regex-2025.9.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:94f6cff6f7e2149c7e6499a6ecd4695379eeda8ccbccb9726e8149f2fe382e92"},
- {file = "regex-2025.9.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:6c0226fb322b82709e78c49cc33484206647f8a39954d7e9de1567f5399becd0"},
- {file = "regex-2025.9.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a12f59c7c380b4fcf7516e9cbb126f95b7a9518902bcf4a852423ff1dcd03e6a"},
- {file = "regex-2025.9.1-cp311-cp311-win32.whl", hash = "sha256:49865e78d147a7a4f143064488da5d549be6bfc3f2579e5044cac61f5c92edd4"},
- {file = "regex-2025.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:d34b901f6f2f02ef60f4ad3855d3a02378c65b094efc4b80388a3aeb700a5de7"},
- {file = "regex-2025.9.1-cp311-cp311-win_arm64.whl", hash = "sha256:47d7c2dab7e0b95b95fd580087b6ae196039d62306a592fa4e162e49004b6299"},
- {file = "regex-2025.9.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:84a25164bd8dcfa9f11c53f561ae9766e506e580b70279d05a7946510bdd6f6a"},
- {file = "regex-2025.9.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:645e88a73861c64c1af558dd12294fb4e67b5c1eae0096a60d7d8a2143a611c7"},
- {file = "regex-2025.9.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:10a450cba5cd5409526ee1d4449f42aad38dd83ac6948cbd6d7f71ca7018f7db"},
- {file = "regex-2025.9.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9dc5991592933a4192c166eeb67b29d9234f9c86344481173d1bc52f73a7104"},
- {file = "regex-2025.9.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a32291add816961aab472f4fad344c92871a2ee33c6c219b6598e98c1f0108f2"},
- {file = "regex-2025.9.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:588c161a68a383478e27442a678e3b197b13c5ba51dbba40c1ccb8c4c7bee9e9"},
- {file = "regex-2025.9.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47829ffaf652f30d579534da9085fe30c171fa2a6744a93d52ef7195dc38218b"},
- {file = "regex-2025.9.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e978e5a35b293ea43f140c92a3269b6ab13fe0a2bf8a881f7ac740f5a6ade85"},
- {file = "regex-2025.9.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4cf09903e72411f4bf3ac1eddd624ecfd423f14b2e4bf1c8b547b72f248b7bf7"},
- {file = "regex-2025.9.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:d016b0f77be63e49613c9e26aaf4a242f196cd3d7a4f15898f5f0ab55c9b24d2"},
- {file = "regex-2025.9.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:656563e620de6908cd1c9d4f7b9e0777e3341ca7db9d4383bcaa44709c90281e"},
- {file = "regex-2025.9.1-cp312-cp312-win32.whl", hash = "sha256:df33f4ef07b68f7ab637b1dbd70accbf42ef0021c201660656601e8a9835de45"},
- {file = "regex-2025.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:5aba22dfbc60cda7c0853516104724dc904caa2db55f2c3e6e984eb858d3edf3"},
- {file = "regex-2025.9.1-cp312-cp312-win_arm64.whl", hash = "sha256:ec1efb4c25e1849c2685fa95da44bfde1b28c62d356f9c8d861d4dad89ed56e9"},
- {file = "regex-2025.9.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:bc6834727d1b98d710a63e6c823edf6ffbf5792eba35d3fa119531349d4142ef"},
- {file = "regex-2025.9.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c3dc05b6d579875719bccc5f3037b4dc80433d64e94681a0061845bd8863c025"},
- {file = "regex-2025.9.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22213527df4c985ec4a729b055a8306272d41d2f45908d7bacb79be0fa7a75ad"},
- {file = "regex-2025.9.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8e3f6e3c5a5a1adc3f7ea1b5aec89abfc2f4fbfba55dafb4343cd1d084f715b2"},
- {file = "regex-2025.9.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bcb89c02a0d6c2bec9b0bb2d8c78782699afe8434493bfa6b4021cc51503f249"},
- {file = "regex-2025.9.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b0e2f95413eb0c651cd1516a670036315b91b71767af83bc8525350d4375ccba"},
- {file = "regex-2025.9.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:09a41dc039e1c97d3c2ed3e26523f748e58c4de3ea7a31f95e1cf9ff973fff5a"},
- {file = "regex-2025.9.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4f0b4258b161094f66857a26ee938d3fe7b8a5063861e44571215c44fbf0e5df"},
- {file = "regex-2025.9.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:bf70e18ac390e6977ea7e56f921768002cb0fa359c4199606c7219854ae332e0"},
- {file = "regex-2025.9.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b84036511e1d2bb0a4ff1aec26951caa2dea8772b223c9e8a19ed8885b32dbac"},
- {file = "regex-2025.9.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c2e05dcdfe224047f2a59e70408274c325d019aad96227ab959403ba7d58d2d7"},
- {file = "regex-2025.9.1-cp313-cp313-win32.whl", hash = "sha256:3b9a62107a7441b81ca98261808fed30ae36ba06c8b7ee435308806bd53c1ed8"},
- {file = "regex-2025.9.1-cp313-cp313-win_amd64.whl", hash = "sha256:b38afecc10c177eb34cfae68d669d5161880849ba70c05cbfbe409f08cc939d7"},
- {file = "regex-2025.9.1-cp313-cp313-win_arm64.whl", hash = "sha256:ec329890ad5e7ed9fc292858554d28d58d56bf62cf964faf0aa57964b21155a0"},
- {file = "regex-2025.9.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:72fb7a016467d364546f22b5ae86c45680a4e0de6b2a6f67441d22172ff641f1"},
- {file = "regex-2025.9.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c9527fa74eba53f98ad86be2ba003b3ebe97e94b6eb2b916b31b5f055622ef03"},
- {file = "regex-2025.9.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c905d925d194c83a63f92422af7544ec188301451b292c8b487f0543726107ca"},
- {file = "regex-2025.9.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:74df7c74a63adcad314426b1f4ea6054a5ab25d05b0244f0c07ff9ce640fa597"},
- {file = "regex-2025.9.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4f6e935e98ea48c7a2e8be44494de337b57a204470e7f9c9c42f912c414cd6f5"},
- {file = "regex-2025.9.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4a62d033cd9ebefc7c5e466731a508dfabee827d80b13f455de68a50d3c2543d"},
- {file = "regex-2025.9.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ef971ebf2b93bdc88d8337238be4dfb851cc97ed6808eb04870ef67589415171"},
- {file = "regex-2025.9.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d936a1db208bdca0eca1f2bb2c1ba1d8370b226785c1e6db76e32a228ffd0ad5"},
- {file = "regex-2025.9.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:7e786d9e4469698fc63815b8de08a89165a0aa851720eb99f5e0ea9d51dd2b6a"},
- {file = "regex-2025.9.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:6b81d7dbc5466ad2c57ce3a0ddb717858fe1a29535c8866f8514d785fdb9fc5b"},
- {file = "regex-2025.9.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:cd4890e184a6feb0ef195338a6ce68906a8903a0f2eb7e0ab727dbc0a3156273"},
- {file = "regex-2025.9.1-cp314-cp314-win32.whl", hash = "sha256:34679a86230e46164c9e0396b56cab13c0505972343880b9e705083cc5b8ec86"},
- {file = "regex-2025.9.1-cp314-cp314-win_amd64.whl", hash = "sha256:a1196e530a6bfa5f4bde029ac5b0295a6ecfaaffbfffede4bbaf4061d9455b70"},
- {file = "regex-2025.9.1-cp314-cp314-win_arm64.whl", hash = "sha256:f46d525934871ea772930e997d577d48c6983e50f206ff7b66d4ac5f8941e993"},
- {file = "regex-2025.9.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a13d20007dce3c4b00af5d84f6c191ed1c0f70928c6d9b6cd7b8d2f125df7f46"},
- {file = "regex-2025.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d6b046b0a01cb713fd53ef36cb59db4b0062b343db28e83b52ac6aa01ee5b368"},
- {file = "regex-2025.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0fa9a7477288717f42dbd02ff5d13057549e9a8cdb81f224c313154cc10bab52"},
- {file = "regex-2025.9.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2b3ad150c6bc01a8cd5030040675060e2adbe6cbc50aadc4da42c6d32ec266e"},
- {file = "regex-2025.9.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:aa88d5a82dfe80deaf04e8c39c8b0ad166d5d527097eb9431cb932c44bf88715"},
- {file = "regex-2025.9.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6f1dae2cf6c2dbc6fd2526653692c144721b3cf3f769d2a3c3aa44d0f38b9a58"},
- {file = "regex-2025.9.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ff62a3022914fc19adaa76b65e03cf62bc67ea16326cbbeb170d280710a7d719"},
- {file = "regex-2025.9.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a34ef82216189d823bc82f614d1031cb0b919abef27cecfd7b07d1e9a8bdeeb4"},
- {file = "regex-2025.9.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6d40e6b49daae9ebbd7fa4e600697372cba85b826592408600068e83a3c47211"},
- {file = "regex-2025.9.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:0aeb0fe80331059c152a002142699a89bf3e44352aee28261315df0c9874759b"},
- {file = "regex-2025.9.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:a90014d29cb3098403d82a879105d1418edbbdf948540297435ea6e377023ea7"},
- {file = "regex-2025.9.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6ff623271e0b0cc5a95b802666bbd70f17ddd641582d65b10fb260cc0c003529"},
- {file = "regex-2025.9.1-cp39-cp39-win32.whl", hash = "sha256:d161bfdeabe236290adfd8c7588da7f835d67e9e7bf2945f1e9e120622839ba6"},
- {file = "regex-2025.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:43ebc77a7dfe36661192afd8d7df5e8be81ec32d2ad0c65b536f66ebfec3dece"},
- {file = "regex-2025.9.1-cp39-cp39-win_arm64.whl", hash = "sha256:5d74b557cf5554001a869cda60b9a619be307df4d10155894aeaad3ee67c9899"},
- {file = "regex-2025.9.1.tar.gz", hash = "sha256:88ac07b38d20b54d79e704e38aa3bd2c0f8027432164226bdee201a1c0c9c9ff"},
-]
-
-[[package]]
-name = "requests"
-version = "2.32.5"
-description = "Python HTTP for Humans."
-optional = false
-python-versions = ">=3.9"
-groups = ["main", "dev", "xla"]
-files = [
- {file = "requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6"},
- {file = "requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf"},
-]
-
-[package.dependencies]
-certifi = ">=2017.4.17"
-charset_normalizer = ">=2,<4"
-idna = ">=2.5,<4"
-urllib3 = ">=1.21.1,<3"
-
-[package.extras]
-socks = ["PySocks (>=1.5.6,!=1.5.7)"]
-use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
-
-[[package]]
-name = "rfc3339-validator"
-version = "0.1.4"
-description = "A pure python RFC3339 validator"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-groups = ["dev"]
-files = [
- {file = "rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa"},
- {file = "rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b"},
-]
-
-[package.dependencies]
-six = "*"
-
-[[package]]
-name = "rfc3986-validator"
-version = "0.1.1"
-description = "Pure python rfc3986 validator"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-groups = ["dev"]
-files = [
- {file = "rfc3986_validator-0.1.1-py2.py3-none-any.whl", hash = "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9"},
- {file = "rfc3986_validator-0.1.1.tar.gz", hash = "sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055"},
-]
-
-[[package]]
-name = "rfc3987-syntax"
-version = "1.1.0"
-description = "Helper functions to syntactically validate strings according to RFC 3987."
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "rfc3987_syntax-1.1.0-py3-none-any.whl", hash = "sha256:6c3d97604e4c5ce9f714898e05401a0445a641cfa276432b0a648c80856f6a3f"},
- {file = "rfc3987_syntax-1.1.0.tar.gz", hash = "sha256:717a62cbf33cffdd16dfa3a497d81ce48a660ea691b1ddd7be710c22f00b4a0d"},
-]
-
-[package.dependencies]
-lark = ">=1.2.2"
-
-[package.extras]
-testing = ["pytest (>=8.3.5)"]
-
-[[package]]
-name = "rich"
-version = "14.1.0"
-description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
-optional = false
-python-versions = ">=3.8.0"
-groups = ["xla"]
-files = [
- {file = "rich-14.1.0-py3-none-any.whl", hash = "sha256:536f5f1785986d6dbdea3c75205c473f970777b4a0d6c6dd1b696aa05a3fa04f"},
- {file = "rich-14.1.0.tar.gz", hash = "sha256:e497a48b844b0320d45007cdebfeaeed8db2a4f4bcf49f15e455cfc4af11eaa8"},
-]
-
-[package.dependencies]
-markdown-it-py = ">=2.2.0"
-pygments = ">=2.13.0,<3.0.0"
-
-[package.extras]
-jupyter = ["ipywidgets (>=7.5.1,<9)"]
-
-[[package]]
-name = "rpds-py"
-version = "0.27.1"
-description = "Python bindings to Rust's persistent data structures (rpds)"
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "rpds_py-0.27.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:68afeec26d42ab3b47e541b272166a0b4400313946871cba3ed3a4fc0cab1cef"},
- {file = "rpds_py-0.27.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74e5b2f7bb6fa38b1b10546d27acbacf2a022a8b5543efb06cfebc72a59c85be"},
- {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9024de74731df54546fab0bfbcdb49fae19159ecaecfc8f37c18d2c7e2c0bd61"},
- {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:31d3ebadefcd73b73928ed0b2fd696f7fefda8629229f81929ac9c1854d0cffb"},
- {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2e7f8f169d775dd9092a1743768d771f1d1300453ddfe6325ae3ab5332b4657"},
- {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d905d16f77eb6ab2e324e09bfa277b4c8e5e6b8a78a3e7ff8f3cdf773b4c013"},
- {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50c946f048209e6362e22576baea09193809f87687a95a8db24e5fbdb307b93a"},
- {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_31_riscv64.whl", hash = "sha256:3deab27804d65cd8289eb814c2c0e807c4b9d9916c9225e363cb0cf875eb67c1"},
- {file = "rpds_py-0.27.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8b61097f7488de4be8244c89915da8ed212832ccf1e7c7753a25a394bf9b1f10"},
- {file = "rpds_py-0.27.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8a3f29aba6e2d7d90528d3c792555a93497fe6538aa65eb675b44505be747808"},
- {file = "rpds_py-0.27.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:dd6cd0485b7d347304067153a6dc1d73f7d4fd995a396ef32a24d24b8ac63ac8"},
- {file = "rpds_py-0.27.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6f4461bf931108c9fa226ffb0e257c1b18dc2d44cd72b125bec50ee0ab1248a9"},
- {file = "rpds_py-0.27.1-cp310-cp310-win32.whl", hash = "sha256:ee5422d7fb21f6a00c1901bf6559c49fee13a5159d0288320737bbf6585bd3e4"},
- {file = "rpds_py-0.27.1-cp310-cp310-win_amd64.whl", hash = "sha256:3e039aabf6d5f83c745d5f9a0a381d031e9ed871967c0a5c38d201aca41f3ba1"},
- {file = "rpds_py-0.27.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:be898f271f851f68b318872ce6ebebbc62f303b654e43bf72683dbdc25b7c881"},
- {file = "rpds_py-0.27.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:62ac3d4e3e07b58ee0ddecd71d6ce3b1637de2d373501412df395a0ec5f9beb5"},
- {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4708c5c0ceb2d034f9991623631d3d23cb16e65c83736ea020cdbe28d57c0a0e"},
- {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:abfa1171a9952d2e0002aba2ad3780820b00cc3d9c98c6630f2e93271501f66c"},
- {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b507d19f817ebaca79574b16eb2ae412e5c0835542c93fe9983f1e432aca195"},
- {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:168b025f8fd8d8d10957405f3fdcef3dc20f5982d398f90851f4abc58c566c52"},
- {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb56c6210ef77caa58e16e8c17d35c63fe3f5b60fd9ba9d424470c3400bcf9ed"},
- {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:d252f2d8ca0195faa707f8eb9368955760880b2b42a8ee16d382bf5dd807f89a"},
- {file = "rpds_py-0.27.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6e5e54da1e74b91dbc7996b56640f79b195d5925c2b78efaa8c5d53e1d88edde"},
- {file = "rpds_py-0.27.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:ffce0481cc6e95e5b3f0a47ee17ffbd234399e6d532f394c8dce320c3b089c21"},
- {file = "rpds_py-0.27.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:a205fdfe55c90c2cd8e540ca9ceba65cbe6629b443bc05db1f590a3db8189ff9"},
- {file = "rpds_py-0.27.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:689fb5200a749db0415b092972e8eba85847c23885c8543a8b0f5c009b1a5948"},
- {file = "rpds_py-0.27.1-cp311-cp311-win32.whl", hash = "sha256:3182af66048c00a075010bc7f4860f33913528a4b6fc09094a6e7598e462fe39"},
- {file = "rpds_py-0.27.1-cp311-cp311-win_amd64.whl", hash = "sha256:b4938466c6b257b2f5c4ff98acd8128ec36b5059e5c8f8372d79316b1c36bb15"},
- {file = "rpds_py-0.27.1-cp311-cp311-win_arm64.whl", hash = "sha256:2f57af9b4d0793e53266ee4325535a31ba48e2f875da81a9177c9926dfa60746"},
- {file = "rpds_py-0.27.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:ae2775c1973e3c30316892737b91f9283f9908e3cc7625b9331271eaaed7dc90"},
- {file = "rpds_py-0.27.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2643400120f55c8a96f7c9d858f7be0c88d383cd4653ae2cf0d0c88f668073e5"},
- {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:16323f674c089b0360674a4abd28d5042947d54ba620f72514d69be4ff64845e"},
- {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a1f4814b65eacac94a00fc9a526e3fdafd78e439469644032032d0d63de4881"},
- {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7ba32c16b064267b22f1850a34051121d423b6f7338a12b9459550eb2096e7ec"},
- {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5c20f33fd10485b80f65e800bbe5f6785af510b9f4056c5a3c612ebc83ba6cb"},
- {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:466bfe65bd932da36ff279ddd92de56b042f2266d752719beb97b08526268ec5"},
- {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:41e532bbdcb57c92ba3be62c42e9f096431b4cf478da9bc3bc6ce5c38ab7ba7a"},
- {file = "rpds_py-0.27.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f149826d742b406579466283769a8ea448eed82a789af0ed17b0cd5770433444"},
- {file = "rpds_py-0.27.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80c60cfb5310677bd67cb1e85a1e8eb52e12529545441b43e6f14d90b878775a"},
- {file = "rpds_py-0.27.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7ee6521b9baf06085f62ba9c7a3e5becffbc32480d2f1b351559c001c38ce4c1"},
- {file = "rpds_py-0.27.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a512c8263249a9d68cac08b05dd59d2b3f2061d99b322813cbcc14c3c7421998"},
- {file = "rpds_py-0.27.1-cp312-cp312-win32.whl", hash = "sha256:819064fa048ba01b6dadc5116f3ac48610435ac9a0058bbde98e569f9e785c39"},
- {file = "rpds_py-0.27.1-cp312-cp312-win_amd64.whl", hash = "sha256:d9199717881f13c32c4046a15f024971a3b78ad4ea029e8da6b86e5aa9cf4594"},
- {file = "rpds_py-0.27.1-cp312-cp312-win_arm64.whl", hash = "sha256:33aa65b97826a0e885ef6e278fbd934e98cdcfed80b63946025f01e2f5b29502"},
- {file = "rpds_py-0.27.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:e4b9fcfbc021633863a37e92571d6f91851fa656f0180246e84cbd8b3f6b329b"},
- {file = "rpds_py-0.27.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1441811a96eadca93c517d08df75de45e5ffe68aa3089924f963c782c4b898cf"},
- {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55266dafa22e672f5a4f65019015f90336ed31c6383bd53f5e7826d21a0e0b83"},
- {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78827d7ac08627ea2c8e02c9e5b41180ea5ea1f747e9db0915e3adf36b62dcf"},
- {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae92443798a40a92dc5f0b01d8a7c93adde0c4dc965310a29ae7c64d72b9fad2"},
- {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c46c9dd2403b66a2a3b9720ec4b74d4ab49d4fabf9f03dfdce2d42af913fe8d0"},
- {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2efe4eb1d01b7f5f1939f4ef30ecea6c6b3521eec451fb93191bf84b2a522418"},
- {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:15d3b4d83582d10c601f481eca29c3f138d44c92187d197aff663a269197c02d"},
- {file = "rpds_py-0.27.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4ed2e16abbc982a169d30d1a420274a709949e2cbdef119fe2ec9d870b42f274"},
- {file = "rpds_py-0.27.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a75f305c9b013289121ec0f1181931975df78738cdf650093e6b86d74aa7d8dd"},
- {file = "rpds_py-0.27.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:67ce7620704745881a3d4b0ada80ab4d99df390838839921f99e63c474f82cf2"},
- {file = "rpds_py-0.27.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9d992ac10eb86d9b6f369647b6a3f412fc0075cfd5d799530e84d335e440a002"},
- {file = "rpds_py-0.27.1-cp313-cp313-win32.whl", hash = "sha256:4f75e4bd8ab8db624e02c8e2fc4063021b58becdbe6df793a8111d9343aec1e3"},
- {file = "rpds_py-0.27.1-cp313-cp313-win_amd64.whl", hash = "sha256:f9025faafc62ed0b75a53e541895ca272815bec18abe2249ff6501c8f2e12b83"},
- {file = "rpds_py-0.27.1-cp313-cp313-win_arm64.whl", hash = "sha256:ed10dc32829e7d222b7d3b93136d25a406ba9788f6a7ebf6809092da1f4d279d"},
- {file = "rpds_py-0.27.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:92022bbbad0d4426e616815b16bc4127f83c9a74940e1ccf3cfe0b387aba0228"},
- {file = "rpds_py-0.27.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:47162fdab9407ec3f160805ac3e154df042e577dd53341745fc7fb3f625e6d92"},
- {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb89bec23fddc489e5d78b550a7b773557c9ab58b7946154a10a6f7a214a48b2"},
- {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e48af21883ded2b3e9eb48cb7880ad8598b31ab752ff3be6457001d78f416723"},
- {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6f5b7bd8e219ed50299e58551a410b64daafb5017d54bbe822e003856f06a802"},
- {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:08f1e20bccf73b08d12d804d6e1c22ca5530e71659e6673bce31a6bb71c1e73f"},
- {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dc5dceeaefcc96dc192e3a80bbe1d6c410c469e97bdd47494a7d930987f18b2"},
- {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_31_riscv64.whl", hash = "sha256:d76f9cc8665acdc0c9177043746775aa7babbf479b5520b78ae4002d889f5c21"},
- {file = "rpds_py-0.27.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:134fae0e36022edad8290a6661edf40c023562964efea0cc0ec7f5d392d2aaef"},
- {file = "rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:eb11a4f1b2b63337cfd3b4d110af778a59aae51c81d195768e353d8b52f88081"},
- {file = "rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:13e608ac9f50a0ed4faec0e90ece76ae33b34c0e8656e3dceb9a7db994c692cd"},
- {file = "rpds_py-0.27.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dd2135527aa40f061350c3f8f89da2644de26cd73e4de458e79606384f4f68e7"},
- {file = "rpds_py-0.27.1-cp313-cp313t-win32.whl", hash = "sha256:3020724ade63fe320a972e2ffd93b5623227e684315adce194941167fee02688"},
- {file = "rpds_py-0.27.1-cp313-cp313t-win_amd64.whl", hash = "sha256:8ee50c3e41739886606388ba3ab3ee2aae9f35fb23f833091833255a31740797"},
- {file = "rpds_py-0.27.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:acb9aafccaae278f449d9c713b64a9e68662e7799dbd5859e2c6b3c67b56d334"},
- {file = "rpds_py-0.27.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b7fb801aa7f845ddf601c49630deeeccde7ce10065561d92729bfe81bd21fb33"},
- {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe0dd05afb46597b9a2e11c351e5e4283c741237e7f617ffb3252780cca9336a"},
- {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b6dfb0e058adb12d8b1d1b25f686e94ffa65d9995a5157afe99743bf7369d62b"},
- {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ed090ccd235f6fa8bb5861684567f0a83e04f52dfc2e5c05f2e4b1309fcf85e7"},
- {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf876e79763eecf3e7356f157540d6a093cef395b65514f17a356f62af6cc136"},
- {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12ed005216a51b1d6e2b02a7bd31885fe317e45897de81d86dcce7d74618ffff"},
- {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:ee4308f409a40e50593c7e3bb8cbe0b4d4c66d1674a316324f0c2f5383b486f9"},
- {file = "rpds_py-0.27.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b08d152555acf1f455154d498ca855618c1378ec810646fcd7c76416ac6dc60"},
- {file = "rpds_py-0.27.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:dce51c828941973a5684d458214d3a36fcd28da3e1875d659388f4f9f12cc33e"},
- {file = "rpds_py-0.27.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:c1476d6f29eb81aa4151c9a31219b03f1f798dc43d8af1250a870735516a1212"},
- {file = "rpds_py-0.27.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:3ce0cac322b0d69b63c9cdb895ee1b65805ec9ffad37639f291dd79467bee675"},
- {file = "rpds_py-0.27.1-cp314-cp314-win32.whl", hash = "sha256:dfbfac137d2a3d0725758cd141f878bf4329ba25e34979797c89474a89a8a3a3"},
- {file = "rpds_py-0.27.1-cp314-cp314-win_amd64.whl", hash = "sha256:a6e57b0abfe7cc513450fcf529eb486b6e4d3f8aee83e92eb5f1ef848218d456"},
- {file = "rpds_py-0.27.1-cp314-cp314-win_arm64.whl", hash = "sha256:faf8d146f3d476abfee026c4ae3bdd9ca14236ae4e4c310cbd1cf75ba33d24a3"},
- {file = "rpds_py-0.27.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:ba81d2b56b6d4911ce735aad0a1d4495e808b8ee4dc58715998741a26874e7c2"},
- {file = "rpds_py-0.27.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:84f7d509870098de0e864cad0102711c1e24e9b1a50ee713b65928adb22269e4"},
- {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e960fc78fecd1100539f14132425e1d5fe44ecb9239f8f27f079962021523e"},
- {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:62f85b665cedab1a503747617393573995dac4600ff51869d69ad2f39eb5e817"},
- {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fed467af29776f6556250c9ed85ea5a4dd121ab56a5f8b206e3e7a4c551e48ec"},
- {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2729615f9d430af0ae6b36cf042cb55c0936408d543fb691e1a9e36648fd35a"},
- {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b207d881a9aef7ba753d69c123a35d96ca7cb808056998f6b9e8747321f03b8"},
- {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:639fd5efec029f99b79ae47e5d7e00ad8a773da899b6309f6786ecaf22948c48"},
- {file = "rpds_py-0.27.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fecc80cb2a90e28af8a9b366edacf33d7a91cbfe4c2c4544ea1246e949cfebeb"},
- {file = "rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:42a89282d711711d0a62d6f57d81aa43a1368686c45bc1c46b7f079d55692734"},
- {file = "rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:cf9931f14223de59551ab9d38ed18d92f14f055a5f78c1d8ad6493f735021bbb"},
- {file = "rpds_py-0.27.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f39f58a27cc6e59f432b568ed8429c7e1641324fbe38131de852cd77b2d534b0"},
- {file = "rpds_py-0.27.1-cp314-cp314t-win32.whl", hash = "sha256:d5fa0ee122dc09e23607a28e6d7b150da16c662e66409bbe85230e4c85bb528a"},
- {file = "rpds_py-0.27.1-cp314-cp314t-win_amd64.whl", hash = "sha256:6567d2bb951e21232c2f660c24cf3470bb96de56cdcb3f071a83feeaff8a2772"},
- {file = "rpds_py-0.27.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:c918c65ec2e42c2a78d19f18c553d77319119bf43aa9e2edf7fb78d624355527"},
- {file = "rpds_py-0.27.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1fea2b1a922c47c51fd07d656324531adc787e415c8b116530a1d29c0516c62d"},
- {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbf94c58e8e0cd6b6f38d8de67acae41b3a515c26169366ab58bdca4a6883bb8"},
- {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c2a8fed130ce946d5c585eddc7c8eeef0051f58ac80a8ee43bd17835c144c2cc"},
- {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:037a2361db72ee98d829bc2c5b7cc55598ae0a5e0ec1823a56ea99374cfd73c1"},
- {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5281ed1cc1d49882f9997981c88df1a22e140ab41df19071222f7e5fc4e72125"},
- {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fd50659a069c15eef8aa3d64bbef0d69fd27bb4a50c9ab4f17f83a16cbf8905"},
- {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_31_riscv64.whl", hash = "sha256:c4b676c4ae3921649a15d28ed10025548e9b561ded473aa413af749503c6737e"},
- {file = "rpds_py-0.27.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:079bc583a26db831a985c5257797b2b5d3affb0386e7ff886256762f82113b5e"},
- {file = "rpds_py-0.27.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4e44099bd522cba71a2c6b97f68e19f40e7d85399de899d66cdb67b32d7cb786"},
- {file = "rpds_py-0.27.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e202e6d4188e53c6661af813b46c37ca2c45e497fc558bacc1a7630ec2695aec"},
- {file = "rpds_py-0.27.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f41f814b8eaa48768d1bb551591f6ba45f87ac76899453e8ccd41dba1289b04b"},
- {file = "rpds_py-0.27.1-cp39-cp39-win32.whl", hash = "sha256:9e71f5a087ead99563c11fdaceee83ee982fd39cf67601f4fd66cb386336ee52"},
- {file = "rpds_py-0.27.1-cp39-cp39-win_amd64.whl", hash = "sha256:71108900c9c3c8590697244b9519017a400d9ba26a36c48381b3f64743a44aab"},
- {file = "rpds_py-0.27.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7ba22cb9693df986033b91ae1d7a979bc399237d45fccf875b76f62bb9e52ddf"},
- {file = "rpds_py-0.27.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5b640501be9288c77738b5492b3fd3abc4ba95c50c2e41273c8a1459f08298d3"},
- {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb08b65b93e0c6dd70aac7f7890a9c0938d5ec71d5cb32d45cf844fb8ae47636"},
- {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d7ff07d696a7a38152ebdb8212ca9e5baab56656749f3d6004b34ab726b550b8"},
- {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fb7c72262deae25366e3b6c0c0ba46007967aea15d1eea746e44ddba8ec58dcc"},
- {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b002cab05d6339716b03a4a3a2ce26737f6231d7b523f339fa061d53368c9d8"},
- {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23f6b69d1c26c4704fec01311963a41d7de3ee0570a84ebde4d544e5a1859ffc"},
- {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:530064db9146b247351f2a0250b8f00b289accea4596a033e94be2389977de71"},
- {file = "rpds_py-0.27.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7b90b0496570bd6b0321724a330d8b545827c4df2034b6ddfc5f5275f55da2ad"},
- {file = "rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:879b0e14a2da6a1102a3fc8af580fc1ead37e6d6692a781bd8c83da37429b5ab"},
- {file = "rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:0d807710df3b5faa66c731afa162ea29717ab3be17bdc15f90f2d9f183da4059"},
- {file = "rpds_py-0.27.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:3adc388fc3afb6540aec081fa59e6e0d3908722771aa1e37ffe22b220a436f0b"},
- {file = "rpds_py-0.27.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c796c0c1cc68cb08b0284db4229f5af76168172670c74908fdbd4b7d7f515819"},
- {file = "rpds_py-0.27.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:cdfe4bb2f9fe7458b7453ad3c33e726d6d1c7c0a72960bcc23800d77384e42df"},
- {file = "rpds_py-0.27.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:8fabb8fd848a5f75a2324e4a84501ee3a5e3c78d8603f83475441866e60b94a3"},
- {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eda8719d598f2f7f3e0f885cba8646644b55a187762bec091fa14a2b819746a9"},
- {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3c64d07e95606ec402a0a1c511fe003873fa6af630bda59bac77fac8b4318ebc"},
- {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93a2ed40de81bcff59aabebb626562d48332f3d028ca2036f1d23cbb52750be4"},
- {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:387ce8c44ae94e0ec50532d9cb0edce17311024c9794eb196b90e1058aadeb66"},
- {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaf94f812c95b5e60ebaf8bfb1898a7d7cb9c1af5744d4a67fa47796e0465d4e"},
- {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:4848ca84d6ded9b58e474dfdbad4b8bfb450344c0551ddc8d958bf4b36aa837c"},
- {file = "rpds_py-0.27.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2bde09cbcf2248b73c7c323be49b280180ff39fadcfe04e7b6f54a678d02a7cf"},
- {file = "rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:94c44ee01fd21c9058f124d2d4f0c9dc7634bec93cd4b38eefc385dabe71acbf"},
- {file = "rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:df8b74962e35c9249425d90144e721eed198e6555a0e22a563d29fe4486b51f6"},
- {file = "rpds_py-0.27.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:dc23e6820e3b40847e2f4a7726462ba0cf53089512abe9ee16318c366494c17a"},
- {file = "rpds_py-0.27.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:aa8933159edc50be265ed22b401125c9eebff3171f570258854dbce3ecd55475"},
- {file = "rpds_py-0.27.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a50431bf02583e21bf273c71b89d710e7a710ad5e39c725b14e685610555926f"},
- {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78af06ddc7fe5cc0e967085a9115accee665fb912c22a3f54bad70cc65b05fe6"},
- {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:70d0738ef8fee13c003b100c2fbd667ec4f133468109b3472d249231108283a3"},
- {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2f6fd8a1cea5bbe599b6e78a6e5ee08db434fc8ffea51ff201c8765679698b3"},
- {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8177002868d1426305bb5de1e138161c2ec9eb2d939be38291d7c431c4712df8"},
- {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:008b839781d6c9bf3b6a8984d1d8e56f0ec46dc56df61fd669c49b58ae800400"},
- {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_31_riscv64.whl", hash = "sha256:a55b9132bb1ade6c734ddd2759c8dc132aa63687d259e725221f106b83a0e485"},
- {file = "rpds_py-0.27.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a46fdec0083a26415f11d5f236b79fa1291c32aaa4a17684d82f7017a1f818b1"},
- {file = "rpds_py-0.27.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:8a63b640a7845f2bdd232eb0d0a4a2dd939bcdd6c57e6bb134526487f3160ec5"},
- {file = "rpds_py-0.27.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:7e32721e5d4922deaaf963469d795d5bde6093207c52fec719bd22e5d1bedbc4"},
- {file = "rpds_py-0.27.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:2c426b99a068601b5f4623573df7a7c3d72e87533a2dd2253353a03e7502566c"},
- {file = "rpds_py-0.27.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4fc9b7fe29478824361ead6e14e4f5aed570d477e06088826537e202d25fe859"},
- {file = "rpds_py-0.27.1.tar.gz", hash = "sha256:26a1c73171d10b7acccbded82bf6a586ab8203601e565badc74bbbf8bc5a10f8"},
-]
-
-[[package]]
-name = "safetensors"
-version = "0.6.2"
-description = ""
-optional = false
-python-versions = ">=3.9"
-groups = ["main"]
-files = [
- {file = "safetensors-0.6.2-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:9c85ede8ec58f120bad982ec47746981e210492a6db876882aa021446af8ffba"},
- {file = "safetensors-0.6.2-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d6675cf4b39c98dbd7d940598028f3742e0375a6b4d4277e76beb0c35f4b843b"},
- {file = "safetensors-0.6.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d2d2b3ce1e2509c68932ca03ab8f20570920cd9754b05063d4368ee52833ecd"},
- {file = "safetensors-0.6.2-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:93de35a18f46b0f5a6a1f9e26d91b442094f2df02e9fd7acf224cfec4238821a"},
- {file = "safetensors-0.6.2-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:89a89b505f335640f9120fac65ddeb83e40f1fd081cb8ed88b505bdccec8d0a1"},
- {file = "safetensors-0.6.2-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fc4d0d0b937e04bdf2ae6f70cd3ad51328635fe0e6214aa1fc811f3b576b3bda"},
- {file = "safetensors-0.6.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8045db2c872db8f4cbe3faa0495932d89c38c899c603f21e9b6486951a5ecb8f"},
- {file = "safetensors-0.6.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:81e67e8bab9878bb568cffbc5f5e655adb38d2418351dc0859ccac158f753e19"},
- {file = "safetensors-0.6.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b0e4d029ab0a0e0e4fdf142b194514695b1d7d3735503ba700cf36d0fc7136ce"},
- {file = "safetensors-0.6.2-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:fa48268185c52bfe8771e46325a1e21d317207bcabcb72e65c6e28e9ffeb29c7"},
- {file = "safetensors-0.6.2-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:d83c20c12c2d2f465997c51b7ecb00e407e5f94d7dec3ea0cc11d86f60d3fde5"},
- {file = "safetensors-0.6.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:d944cea65fad0ead848b6ec2c37cc0b197194bec228f8020054742190e9312ac"},
- {file = "safetensors-0.6.2-cp38-abi3-win32.whl", hash = "sha256:cab75ca7c064d3911411461151cb69380c9225798a20e712b102edda2542ddb1"},
- {file = "safetensors-0.6.2-cp38-abi3-win_amd64.whl", hash = "sha256:c7b214870df923cbc1593c3faee16bec59ea462758699bd3fee399d00aac072c"},
- {file = "safetensors-0.6.2.tar.gz", hash = "sha256:43ff2aa0e6fa2dc3ea5524ac7ad93a9839256b8703761e76e2d0b2a3fa4f15d9"},
-]
-
-[package.extras]
-all = ["safetensors[jax]", "safetensors[numpy]", "safetensors[paddlepaddle]", "safetensors[pinned-tf]", "safetensors[quality]", "safetensors[testing]", "safetensors[torch]"]
-dev = ["safetensors[all]"]
-jax = ["flax (>=0.6.3)", "jax (>=0.3.25)", "jaxlib (>=0.3.25)", "safetensors[numpy]"]
-mlx = ["mlx (>=0.0.9)"]
-numpy = ["numpy (>=1.21.6)"]
-paddlepaddle = ["paddlepaddle (>=2.4.1)", "safetensors[numpy]"]
-pinned-tf = ["safetensors[numpy]", "tensorflow (==2.18.0)"]
-quality = ["ruff"]
-tensorflow = ["safetensors[numpy]", "tensorflow (>=2.11.0)"]
-testing = ["h5py (>=3.7.0)", "huggingface-hub (>=0.12.1)", "hypothesis (>=6.70.2)", "pytest (>=7.2.0)", "pytest-benchmark (>=4.0.0)", "safetensors[numpy]", "setuptools-rust (>=1.5.2)"]
-testingfree = ["huggingface-hub (>=0.12.1)", "hypothesis (>=6.70.2)", "pytest (>=7.2.0)", "pytest-benchmark (>=4.0.0)", "safetensors[numpy]", "setuptools-rust (>=1.5.2)"]
-torch = ["safetensors[numpy]", "torch (>=1.10)"]
-
-[[package]]
-name = "send2trash"
-version = "1.8.3"
-description = "Send file to trash natively under Mac OS X, Windows and Linux"
-optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
-groups = ["dev"]
-files = [
- {file = "Send2Trash-1.8.3-py3-none-any.whl", hash = "sha256:0c31227e0bd08961c7665474a3d1ef7193929fedda4233843689baa056be46c9"},
- {file = "Send2Trash-1.8.3.tar.gz", hash = "sha256:b18e7a3966d99871aefeb00cfbcfdced55ce4871194810fc71f4aa484b953abf"},
-]
-
-[package.extras]
-nativelib = ["pyobjc-framework-Cocoa ; sys_platform == \"darwin\"", "pywin32 ; sys_platform == \"win32\""]
-objc = ["pyobjc-framework-Cocoa ; sys_platform == \"darwin\""]
-win32 = ["pywin32 ; sys_platform == \"win32\""]
-
-[[package]]
-name = "setuptools"
-version = "80.9.0"
-description = "Easily download, build, install, upgrade, and uninstall Python packages"
-optional = false
-python-versions = ">=3.9"
-groups = ["main", "dev"]
-files = [
- {file = "setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922"},
- {file = "setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c"},
-]
-markers = {main = "platform_system == \"Linux\" and platform_machine == \"x86_64\" or python_version >= \"3.12\""}
-
-[package.extras]
-check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""]
-core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"]
-cover = ["pytest-cov"]
-doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"]
-enabler = ["pytest-enabler (>=2.2)"]
-test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"]
-type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.14.*)", "pytest-mypy"]
-
-[[package]]
-name = "six"
-version = "1.17.0"
-description = "Python 2 and 3 compatibility utilities"
-optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
-groups = ["dev"]
-files = [
- {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"},
- {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"},
-]
-
-[[package]]
-name = "smmap"
-version = "5.0.2"
-description = "A pure Python implementation of a sliding window memory map manager"
-optional = false
-python-versions = ">=3.7"
-groups = ["dev"]
-files = [
- {file = "smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e"},
- {file = "smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5"},
-]
-
-[[package]]
-name = "sniffio"
-version = "1.3.1"
-description = "Sniff out which async library your code is running under"
-optional = false
-python-versions = ">=3.7"
-groups = ["dev"]
-files = [
- {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"},
- {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"},
-]
-
-[[package]]
-name = "soupsieve"
-version = "2.8"
-description = "A modern CSS selector implementation for Beautiful Soup."
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "soupsieve-2.8-py3-none-any.whl", hash = "sha256:0cc76456a30e20f5d7f2e14a98a4ae2ee4e5abdc7c5ea0aafe795f344bc7984c"},
- {file = "soupsieve-2.8.tar.gz", hash = "sha256:e2dd4a40a628cb5f28f6d4b0db8800b8f581b65bb380b97de22ba5ca8d72572f"},
-]
-
-[[package]]
-name = "stack-data"
-version = "0.6.3"
-description = "Extract data from python stack frames and tracebacks for informative displays"
-optional = false
-python-versions = "*"
-groups = ["dev"]
-files = [
- {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"},
- {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"},
-]
-
-[package.dependencies]
-asttokens = ">=2.1.0"
-executing = ">=1.2.0"
-pure-eval = "*"
-
-[package.extras]
-tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"]
-
-[[package]]
-name = "streamlit"
-version = "1.49.1"
-description = "A faster way to build and share data apps"
-optional = false
-python-versions = "!=3.9.7,>=3.9"
-groups = ["dev"]
-files = [
- {file = "streamlit-1.49.1-py3-none-any.whl", hash = "sha256:ad7b6d0dc35db168587acf96f80378249467fc057ed739a41c511f6bf5aa173b"},
- {file = "streamlit-1.49.1.tar.gz", hash = "sha256:6f213f1e43f035143a56f58ad50068d8a09482f0a2dad1050d7e7e99a9689818"},
-]
-
-[package.dependencies]
-altair = ">=4.0,<5.4.0 || >5.4.0,<5.4.1 || >5.4.1,<6"
-blinker = ">=1.5.0,<2"
-cachetools = ">=4.0,<7"
-click = ">=7.0,<9"
-gitpython = ">=3.0.7,<3.1.19 || >3.1.19,<4"
-numpy = ">=1.23,<3"
-packaging = ">=20,<26"
-pandas = ">=1.4.0,<3"
-pillow = ">=7.1.0,<12"
-protobuf = ">=3.20,<7"
-pyarrow = ">=7.0"
-pydeck = ">=0.8.0b4,<1"
-requests = ">=2.27,<3"
-tenacity = ">=8.1.0,<10"
-toml = ">=0.10.1,<2"
-tornado = ">=6.0.3,<6.5.0 || >6.5.0,<7"
-typing-extensions = ">=4.4.0,<5"
-watchdog = {version = ">=2.1.5,<7", markers = "platform_system != \"Darwin\""}
-
-[package.extras]
-all = ["rich (>=11.0.0)", "streamlit[auth,charts,pdf,snowflake,sql]"]
-auth = ["Authlib (>=1.3.2)"]
-charts = ["graphviz (>=0.19.0)", "matplotlib (>=3.0.0)", "orjson (>=3.5.0)", "plotly (>=4.0.0)"]
-pdf = ["streamlit-pdf (>=1.0.0)"]
-snowflake = ["snowflake-connector-python (>=3.3.0) ; python_version < \"3.12\"", "snowflake-snowpark-python[modin] (>=1.17.0) ; python_version < \"3.12\""]
-sql = ["SQLAlchemy (>=2.0.0)"]
-
-[[package]]
-name = "sympy"
-version = "1.14.0"
-description = "Computer algebra system (CAS) in Python"
-optional = false
-python-versions = ">=3.9"
-groups = ["main"]
-files = [
- {file = "sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5"},
- {file = "sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517"},
-]
-
-[package.dependencies]
-mpmath = ">=1.1.0,<1.4"
-
-[package.extras]
-dev = ["hypothesis (>=6.70.0)", "pytest (>=7.1.0)"]
-
-[[package]]
-name = "tabulate"
-version = "0.9.0"
-description = "Pretty-print tabular data"
-optional = false
-python-versions = ">=3.7"
-groups = ["dev"]
-files = [
- {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"},
- {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"},
-]
-
-[package.extras]
-widechars = ["wcwidth"]
-
-[[package]]
-name = "tenacity"
-version = "9.1.2"
-description = "Retry code until it succeeds"
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138"},
- {file = "tenacity-9.1.2.tar.gz", hash = "sha256:1169d376c297e7de388d18b4481760d478b0e99a777cad3a9c86e556f4b697cb"},
-]
-
-[package.extras]
-doc = ["reno", "sphinx"]
-test = ["pytest", "tornado (>=4.5)", "typeguard"]
-
-[[package]]
-name = "terminado"
-version = "0.18.1"
-description = "Tornado websocket backend for the Xterm.js Javascript terminal emulator library."
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "terminado-0.18.1-py3-none-any.whl", hash = "sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0"},
- {file = "terminado-0.18.1.tar.gz", hash = "sha256:de09f2c4b85de4765f7714688fff57d3e75bad1f909b589fde880460c753fd2e"},
-]
-
-[package.dependencies]
-ptyprocess = {version = "*", markers = "os_name != \"nt\""}
-pywinpty = {version = ">=1.1.0", markers = "os_name == \"nt\""}
-tornado = ">=6.1.0"
-
-[package.extras]
-docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"]
-test = ["pre-commit", "pytest (>=7.0)", "pytest-timeout"]
-typing = ["mypy (>=1.6,<2.0)", "traitlets (>=5.11.1)"]
-
-[[package]]
-name = "tinycss2"
-version = "1.4.0"
-description = "A tiny CSS parser"
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "tinycss2-1.4.0-py3-none-any.whl", hash = "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289"},
- {file = "tinycss2-1.4.0.tar.gz", hash = "sha256:10c0972f6fc0fbee87c3edb76549357415e94548c1ae10ebccdea16fb404a9b7"},
-]
-
-[package.dependencies]
-webencodings = ">=0.4"
-
-[package.extras]
-doc = ["sphinx", "sphinx_rtd_theme"]
-test = ["pytest", "ruff"]
-
-[[package]]
-name = "tokenizers"
-version = "0.22.0"
-description = ""
-optional = false
-python-versions = ">=3.9"
-groups = ["main"]
-files = [
- {file = "tokenizers-0.22.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:eaa9620122a3fb99b943f864af95ed14c8dfc0f47afa3b404ac8c16b3f2bb484"},
- {file = "tokenizers-0.22.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:71784b9ab5bf0ff3075bceeb198149d2c5e068549c0d18fe32d06ba0deb63f79"},
- {file = "tokenizers-0.22.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec5b71f668a8076802b0241a42387d48289f25435b86b769ae1837cad4172a17"},
- {file = "tokenizers-0.22.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ea8562fa7498850d02a16178105b58803ea825b50dc9094d60549a7ed63654bb"},
- {file = "tokenizers-0.22.0-cp39-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4136e1558a9ef2e2f1de1555dcd573e1cbc4a320c1a06c4107a3d46dc8ac6e4b"},
- {file = "tokenizers-0.22.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cdf5954de3962a5fd9781dc12048d24a1a6f1f5df038c6e95db328cd22964206"},
- {file = "tokenizers-0.22.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8337ca75d0731fc4860e6204cc24bb36a67d9736142aa06ed320943b50b1e7ed"},
- {file = "tokenizers-0.22.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a89264e26f63c449d8cded9061adea7b5de53ba2346fc7e87311f7e4117c1cc8"},
- {file = "tokenizers-0.22.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:790bad50a1b59d4c21592f9c3cf5e5cf9c3c7ce7e1a23a739f13e01fb1be377a"},
- {file = "tokenizers-0.22.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:76cf6757c73a10ef10bf06fa937c0ec7393d90432f543f49adc8cab3fb6f26cb"},
- {file = "tokenizers-0.22.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:1626cb186e143720c62c6c6b5371e62bbc10af60481388c0da89bc903f37ea0c"},
- {file = "tokenizers-0.22.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:da589a61cbfea18ae267723d6b029b84598dc8ca78db9951d8f5beff72d8507c"},
- {file = "tokenizers-0.22.0-cp39-abi3-win32.whl", hash = "sha256:dbf9d6851bddae3e046fedfb166f47743c1c7bd11c640f0691dd35ef0bcad3be"},
- {file = "tokenizers-0.22.0-cp39-abi3-win_amd64.whl", hash = "sha256:c78174859eeaee96021f248a56c801e36bfb6bd5b067f2e95aa82445ca324f00"},
- {file = "tokenizers-0.22.0.tar.gz", hash = "sha256:2e33b98525be8453f355927f3cab312c36cd3e44f4d7e9e97da2fa94d0a49dcb"},
-]
-
-[package.dependencies]
-huggingface-hub = ">=0.16.4,<1.0"
-
-[package.extras]
-dev = ["tokenizers[testing]"]
-docs = ["setuptools-rust", "sphinx", "sphinx-rtd-theme"]
-testing = ["black (==22.3)", "datasets", "numpy", "pytest", "pytest-asyncio", "requests", "ruff"]
-
-[[package]]
-name = "toml"
-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"]
-files = [
- {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
- {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
-]
-
-[[package]]
-name = "tomli"
-version = "2.2.1"
-description = "A lil' TOML parser"
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-markers = "python_version == \"3.10\""
-files = [
- {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"},
- {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"},
- {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"},
- {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"},
- {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"},
- {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"},
- {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"},
- {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"},
- {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"},
- {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"},
- {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"},
- {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"},
- {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"},
- {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"},
- {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"},
- {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"},
- {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"},
- {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"},
- {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"},
- {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"},
- {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"},
- {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"},
- {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"},
- {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"},
- {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"},
- {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"},
- {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"},
- {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"},
- {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"},
- {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"},
- {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"},
- {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"},
-]
-
-[[package]]
-name = "torch"
-version = "2.7.1"
-description = "Tensors and Dynamic neural networks in Python with strong GPU acceleration"
-optional = false
-python-versions = ">=3.9.0"
-groups = ["main"]
-markers = "python_version >= \"3.14\""
-files = [
- {file = "torch-2.7.1-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:a103b5d782af5bd119b81dbcc7ffc6fa09904c423ff8db397a1e6ea8fd71508f"},
- {file = "torch-2.7.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:fe955951bdf32d182ee8ead6c3186ad54781492bf03d547d31771a01b3d6fb7d"},
- {file = "torch-2.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:885453d6fba67d9991132143bf7fa06b79b24352f4506fd4d10b309f53454162"},
- {file = "torch-2.7.1-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:d72acfdb86cee2a32c0ce0101606f3758f0d8bb5f8f31e7920dc2809e963aa7c"},
- {file = "torch-2.7.1-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:236f501f2e383f1cb861337bdf057712182f910f10aeaf509065d54d339e49b2"},
- {file = "torch-2.7.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:06eea61f859436622e78dd0cdd51dbc8f8c6d76917a9cf0555a333f9eac31ec1"},
- {file = "torch-2.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:8273145a2e0a3c6f9fd2ac36762d6ee89c26d430e612b95a99885df083b04e52"},
- {file = "torch-2.7.1-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:aea4fc1bf433d12843eb2c6b2204861f43d8364597697074c8d38ae2507f8730"},
- {file = "torch-2.7.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:27ea1e518df4c9de73af7e8a720770f3628e7f667280bce2be7a16292697e3fa"},
- {file = "torch-2.7.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:c33360cfc2edd976c2633b3b66c769bdcbbf0e0b6550606d188431c81e7dd1fc"},
- {file = "torch-2.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:d8bf6e1856ddd1807e79dc57e54d3335f2b62e6f316ed13ed3ecfe1fc1df3d8b"},
- {file = "torch-2.7.1-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:787687087412c4bd68d315e39bc1223f08aae1d16a9e9771d95eabbb04ae98fb"},
- {file = "torch-2.7.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:03563603d931e70722dce0e11999d53aa80a375a3d78e6b39b9f6805ea0a8d28"},
- {file = "torch-2.7.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:d632f5417b6980f61404a125b999ca6ebd0b8b4bbdbb5fbbba44374ab619a412"},
- {file = "torch-2.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:23660443e13995ee93e3d844786701ea4ca69f337027b05182f5ba053ce43b38"},
- {file = "torch-2.7.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:0da4f4dba9f65d0d203794e619fe7ca3247a55ffdcbd17ae8fb83c8b2dc9b585"},
- {file = "torch-2.7.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:e08d7e6f21a617fe38eeb46dd2213ded43f27c072e9165dc27300c9ef9570934"},
- {file = "torch-2.7.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:30207f672328a42df4f2174b8f426f354b2baa0b7cca3a0adb3d6ab5daf00dc8"},
- {file = "torch-2.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:79042feca1c634aaf6603fe6feea8c6b30dfa140a6bbc0b973e2260c7e79a22e"},
- {file = "torch-2.7.1-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:988b0cbc4333618a1056d2ebad9eb10089637b659eb645434d0809d8d937b946"},
- {file = "torch-2.7.1-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:e0d81e9a12764b6f3879a866607c8ae93113cbcad57ce01ebde63eb48a576369"},
- {file = "torch-2.7.1-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:8394833c44484547ed4a47162318337b88c97acdb3273d85ea06e03ffff44998"},
- {file = "torch-2.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:df41989d9300e6e3c19ec9f56f856187a6ef060c3662fe54f4b6baf1fc90bd19"},
- {file = "torch-2.7.1-cp39-none-macosx_11_0_arm64.whl", hash = "sha256:a737b5edd1c44a5c1ece2e9f3d00df9d1b3fb9541138bee56d83d38293fb6c9d"},
-]
-
-[package.dependencies]
-filelock = "*"
-fsspec = "*"
-jinja2 = "*"
-networkx = "*"
-nvidia-cublas-cu12 = {version = "12.6.4.1", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
-nvidia-cuda-cupti-cu12 = {version = "12.6.80", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
-nvidia-cuda-nvrtc-cu12 = {version = "12.6.77", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
-nvidia-cuda-runtime-cu12 = {version = "12.6.77", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
-nvidia-cudnn-cu12 = {version = "9.5.1.17", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
-nvidia-cufft-cu12 = {version = "11.3.0.4", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
-nvidia-cufile-cu12 = {version = "1.11.1.6", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
-nvidia-curand-cu12 = {version = "10.3.7.77", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
-nvidia-cusolver-cu12 = {version = "11.7.1.2", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
-nvidia-cusparse-cu12 = {version = "12.5.4.2", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
-nvidia-cusparselt-cu12 = {version = "0.6.3", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
-nvidia-nccl-cu12 = {version = "2.26.2", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
-nvidia-nvjitlink-cu12 = {version = "12.6.85", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
-nvidia-nvtx-cu12 = {version = "12.6.77", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
-setuptools = {version = "*", markers = "python_version >= \"3.12\""}
-sympy = ">=1.13.3"
-triton = {version = "3.3.1", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
-typing-extensions = ">=4.10.0"
-
-[package.extras]
-opt-einsum = ["opt-einsum (>=3.3)"]
-optree = ["optree (>=0.13.0)"]
-
-[[package]]
-name = "torch"
-version = "2.8.0"
-description = "Tensors and Dynamic neural networks in Python with strong GPU acceleration"
-optional = false
-python-versions = ">=3.9.0"
-groups = ["main"]
-markers = "python_version < \"3.14\""
-files = [
- {file = "torch-2.8.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:0be92c08b44009d4131d1ff7a8060d10bafdb7ddcb7359ef8d8c5169007ea905"},
- {file = "torch-2.8.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:89aa9ee820bb39d4d72b794345cccef106b574508dd17dbec457949678c76011"},
- {file = "torch-2.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:e8e5bf982e87e2b59d932769938b698858c64cc53753894be25629bdf5cf2f46"},
- {file = "torch-2.8.0-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:a3f16a58a9a800f589b26d47ee15aca3acf065546137fc2af039876135f4c760"},
- {file = "torch-2.8.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:220a06fd7af8b653c35d359dfe1aaf32f65aa85befa342629f716acb134b9710"},
- {file = "torch-2.8.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:c12fa219f51a933d5f80eeb3a7a5d0cbe9168c0a14bbb4055f1979431660879b"},
- {file = "torch-2.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:8c7ef765e27551b2fbfc0f41bcf270e1292d9bf79f8e0724848b1682be6e80aa"},
- {file = "torch-2.8.0-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:5ae0524688fb6707c57a530c2325e13bb0090b745ba7b4a2cd6a3ce262572916"},
- {file = "torch-2.8.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:e2fab4153768d433f8ed9279c8133a114a034a61e77a3a104dcdf54388838705"},
- {file = "torch-2.8.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b2aca0939fb7e4d842561febbd4ffda67a8e958ff725c1c27e244e85e982173c"},
- {file = "torch-2.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:2f4ac52f0130275d7517b03a33d2493bab3693c83dcfadf4f81688ea82147d2e"},
- {file = "torch-2.8.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:619c2869db3ada2c0105487ba21b5008defcc472d23f8b80ed91ac4a380283b0"},
- {file = "torch-2.8.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:2b2f96814e0345f5a5aed9bf9734efa913678ed19caf6dc2cddb7930672d6128"},
- {file = "torch-2.8.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:65616ca8ec6f43245e1f5f296603e33923f4c30f93d65e103d9e50c25b35150b"},
- {file = "torch-2.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:659df54119ae03e83a800addc125856effda88b016dfc54d9f65215c3975be16"},
- {file = "torch-2.8.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:1a62a1ec4b0498930e2543535cf70b1bef8c777713de7ceb84cd79115f553767"},
- {file = "torch-2.8.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:83c13411a26fac3d101fe8035a6b0476ae606deb8688e904e796a3534c197def"},
- {file = "torch-2.8.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:8f0a9d617a66509ded240add3754e462430a6c1fc5589f86c17b433dd808f97a"},
- {file = "torch-2.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:a7242b86f42be98ac674b88a4988643b9bc6145437ec8f048fea23f72feb5eca"},
- {file = "torch-2.8.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:7b677e17f5a3e69fdef7eb3b9da72622f8d322692930297e4ccb52fefc6c8211"},
- {file = "torch-2.8.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:da6afa31c13b669d4ba49d8a2169f0db2c3ec6bec4af898aa714f401d4c38904"},
- {file = "torch-2.8.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:06fcee8000e5c62a9f3e52a688b9c5abb7c6228d0e56e3452983416025c41381"},
- {file = "torch-2.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:5128fe752a355d9308e56af1ad28b15266fe2da5948660fad44de9e3a9e36e8c"},
- {file = "torch-2.8.0-cp39-none-macosx_11_0_arm64.whl", hash = "sha256:e9f071f5b52a9f6970dc8a919694b27a91ae9dc08898b2b988abbef5eddfd1ae"},
-]
-
-[package.dependencies]
-filelock = "*"
-fsspec = "*"
-jinja2 = "*"
-networkx = "*"
-nvidia-cublas-cu12 = {version = "12.8.4.1", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
-nvidia-cuda-cupti-cu12 = {version = "12.8.90", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
-nvidia-cuda-nvrtc-cu12 = {version = "12.8.93", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
-nvidia-cuda-runtime-cu12 = {version = "12.8.90", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
-nvidia-cudnn-cu12 = {version = "9.10.2.21", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
-nvidia-cufft-cu12 = {version = "11.3.3.83", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
-nvidia-cufile-cu12 = {version = "1.13.1.3", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
-nvidia-curand-cu12 = {version = "10.3.9.90", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
-nvidia-cusolver-cu12 = {version = "11.7.3.90", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
-nvidia-cusparse-cu12 = {version = "12.5.8.93", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
-nvidia-cusparselt-cu12 = {version = "0.7.1", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
-nvidia-nccl-cu12 = {version = "2.27.3", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
-nvidia-nvjitlink-cu12 = {version = "12.8.93", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
-nvidia-nvtx-cu12 = {version = "12.8.90", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
-setuptools = {version = "*", markers = "python_version >= \"3.12\""}
-sympy = ">=1.13.3"
-triton = {version = "3.4.0", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""}
-typing-extensions = ">=4.10.0"
-
-[package.extras]
-opt-einsum = ["opt-einsum (>=3.3)"]
-optree = ["optree (>=0.13.0)"]
-pyyaml = ["pyyaml"]
-
-[[package]]
-name = "torch-xla"
-version = "2.8.0"
-description = "XLA bridge for PyTorch"
-optional = false
-python-versions = ">=3.10.0"
-groups = ["xla"]
-files = [
- {file = "torch_xla-2.8.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:548adfde13b5e3539cb451a5c0170db8e3d4364fe79de14139f353401e58116f"},
- {file = "torch_xla-2.8.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:92af0ed9aff7983a35df4eb6b744a33ea881d0cbcb674e89b46f11285d3062cb"},
- {file = "torch_xla-2.8.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:08dd1cbd227ef712d8af4879cdf1560b82a153475f6a97d5b749b710b3ad7e9d"},
- {file = "torch_xla-2.8.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:be2388ba3fa875892eb02d8a3e80ed2cd3878f99806639caebdf2b522f9fdb6c"},
-]
-
-[package.dependencies]
-absl-py = ">=1.0.0"
-libtpu = {version = "0.0.17", optional = true, markers = "extra == \"tpu\""}
-numpy = "*"
-pyyaml = "*"
-requests = "*"
-tpu-info = {version = "*", optional = true, markers = "extra == \"tpu\""}
-
-[package.extras]
-pallas = ["jax (==0.6.2)", "jaxlib (==0.6.2)"]
-tpu = ["libtpu (==0.0.17)", "tpu-info"]
-
-[[package]]
-name = "tornado"
-version = "6.5.2"
-description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed."
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "tornado-6.5.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:2436822940d37cde62771cff8774f4f00b3c8024fe482e16ca8387b8a2724db6"},
- {file = "tornado-6.5.2-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:583a52c7aa94ee046854ba81d9ebb6c81ec0fd30386d96f7640c96dad45a03ef"},
- {file = "tornado-6.5.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0fe179f28d597deab2842b86ed4060deec7388f1fd9c1b4a41adf8af058907e"},
- {file = "tornado-6.5.2-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b186e85d1e3536d69583d2298423744740986018e393d0321df7340e71898882"},
- {file = "tornado-6.5.2-cp39-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e792706668c87709709c18b353da1f7662317b563ff69f00bab83595940c7108"},
- {file = "tornado-6.5.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:06ceb1300fd70cb20e43b1ad8aaee0266e69e7ced38fa910ad2e03285009ce7c"},
- {file = "tornado-6.5.2-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:74db443e0f5251be86cbf37929f84d8c20c27a355dd452a5cfa2aada0d001ec4"},
- {file = "tornado-6.5.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b5e735ab2889d7ed33b32a459cac490eda71a1ba6857b0118de476ab6c366c04"},
- {file = "tornado-6.5.2-cp39-abi3-win32.whl", hash = "sha256:c6f29e94d9b37a95013bb669616352ddb82e3bfe8326fccee50583caebc8a5f0"},
- {file = "tornado-6.5.2-cp39-abi3-win_amd64.whl", hash = "sha256:e56a5af51cc30dd2cae649429af65ca2f6571da29504a07995175df14c18f35f"},
- {file = "tornado-6.5.2-cp39-abi3-win_arm64.whl", hash = "sha256:d6c33dc3672e3a1f3618eb63b7ef4683a7688e7b9e6e8f0d9aa5726360a004af"},
- {file = "tornado-6.5.2.tar.gz", hash = "sha256:ab53c8f9a0fa351e2c0741284e06c7a45da86afb544133201c5cc8578eb076a0"},
-]
-
-[[package]]
-name = "tpu-info"
-version = "0.5.1"
-description = "CLI tool to view TPU metrics"
-optional = false
-python-versions = ">=3.8"
-groups = ["xla"]
-files = [
- {file = "tpu_info-0.5.1-py3-none-any.whl", hash = "sha256:68c46edd1fe79a849526b27c9dd1893e8114be2529cfa1bcec20a95afc579b0f"},
-]
-
-[package.dependencies]
-grpcio = ">=1.65.5"
-packaging = "*"
-protobuf = "*"
-rich = "*"
-
-[package.extras]
-test = ["absl-py"]
-
-[[package]]
-name = "tqdm"
-version = "4.67.1"
-description = "Fast, Extensible Progress Meter"
-optional = false
-python-versions = ">=3.7"
-groups = ["main", "dev"]
-files = [
- {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"},
- {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"},
-]
-
-[package.dependencies]
-colorama = {version = "*", markers = "platform_system == \"Windows\""}
-
-[package.extras]
-dev = ["nbval", "pytest (>=6)", "pytest-asyncio (>=0.24)", "pytest-cov", "pytest-timeout"]
-discord = ["requests"]
-notebook = ["ipywidgets (>=6)"]
-slack = ["slack-sdk"]
-telegram = ["requests"]
-
-[[package]]
-name = "traitlets"
-version = "5.14.3"
-description = "Traitlets Python configuration system"
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f"},
- {file = "traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7"},
-]
-
-[package.extras]
-docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"]
-test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"]
-
-[[package]]
-name = "transformers"
-version = "4.56.1"
-description = "State-of-the-art Machine Learning for JAX, PyTorch and TensorFlow"
-optional = false
-python-versions = ">=3.9.0"
-groups = ["main"]
-files = [
- {file = "transformers-4.56.1-py3-none-any.whl", hash = "sha256:1697af6addfb6ddbce9618b763f4b52d5a756f6da4899ffd1b4febf58b779248"},
- {file = "transformers-4.56.1.tar.gz", hash = "sha256:0d88b1089a563996fc5f2c34502f10516cad3ea1aa89f179f522b54c8311fe74"},
-]
-
-[package.dependencies]
-filelock = "*"
-huggingface-hub = ">=0.34.0,<1.0"
-numpy = ">=1.17"
-packaging = ">=20.0"
-pyyaml = ">=5.1"
-regex = "!=2019.12.17"
-requests = "*"
-safetensors = ">=0.4.3"
-tokenizers = ">=0.22.0,<=0.23.0"
-tqdm = ">=4.27"
-
-[package.extras]
-accelerate = ["accelerate (>=0.26.0)"]
-all = ["Pillow (>=10.0.1,<=15.0)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "accelerate (>=0.26.0)", "av", "codecarbon (>=2.8.1)", "flax (>=0.4.1,<=0.7.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "jinja2 (>=3.1.0)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "kernels (>=0.6.1,<=0.9)", "librosa", "mistral-common[opencv] (>=1.6.3)", "num2words", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "phonemizer", "protobuf", "pyctcdecode (>=0.4.0)", "ray[tune] (>=2.7.0)", "scipy (<1.13.0)", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timm (!=1.0.18,<=1.0.19)", "tokenizers (>=0.22.0,<=0.23.0)", "torch (>=2.2)", "torchaudio", "torchvision"]
-audio = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"]
-benchmark = ["optimum-benchmark (>=0.3.0)"]
-chat-template = ["jinja2 (>=3.1.0)"]
-codecarbon = ["codecarbon (>=2.8.1)"]
-deepspeed = ["accelerate (>=0.26.0)", "deepspeed (>=0.9.3)"]
-deepspeed-testing = ["GitPython (<3.1.19)", "accelerate (>=0.26.0)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (>=2.15.0)", "datasets (>=2.15.0)", "deepspeed (>=0.9.3)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "libcst", "mistral-common[opencv] (>=1.6.3)", "nltk (<=3.8.1)", "optuna", "parameterized (>=0.9)", "protobuf", "psutil", "pydantic (>=2)", "pytest (>=7.2.0)", "pytest-asyncio", "pytest-order", "pytest-rerunfailures", "pytest-rich", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.11.2)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "sentencepiece (>=0.1.91,!=0.1.92)", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorboard", "timeout-decorator"]
-dev = ["GitPython (<3.1.19)", "GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "accelerate (>=0.26.0)", "av", "beautifulsoup4", "codecarbon (>=2.8.1)", "cookiecutter (==1.7.3)", "cookiecutter (==1.7.3)", "datasets (>=2.15.0)", "datasets (>=2.15.0)", "datasets (>=2.15.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "flax (>=0.4.1,<=0.7.0)", "fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "jinja2 (>=3.1.0)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "kernels (>=0.6.1,<=0.9)", "libcst", "libcst", "librosa", "mistral-common[opencv] (>=1.6.3)", "mistral-common[opencv] (>=1.6.3)", "nltk (<=3.8.1)", "num2words", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "pandas (<2.3.0)", "parameterized (>=0.9)", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic (>=2)", "pytest (>=7.2.0)", "pytest-asyncio", "pytest-order", "pytest-rerunfailures", "pytest-rich", "pytest-timeout", "pytest-xdist", "ray[tune] (>=2.7.0)", "rhoknp (>=1.1.0,<1.3.1)", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.11.2)", "ruff (==0.11.2)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "scipy (<1.13.0)", "sentencepiece (>=0.1.91,!=0.1.92)", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict_core (>=20220729)", "sudachipy (>=0.6.6)", "tensorboard", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timeout-decorator", "timm (!=1.0.18,<=1.0.19)", "tokenizers (>=0.22.0,<=0.23.0)", "torch (>=2.2)", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic_lite (>=1.0.7)", "urllib3 (<2.0.0)"]
-dev-tensorflow = ["GitPython (<3.1.19)", "GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "beautifulsoup4", "cookiecutter (==1.7.3)", "cookiecutter (==1.7.3)", "datasets (>=2.15.0)", "datasets (>=2.15.0)", "datasets (>=2.15.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "libcst", "libcst", "librosa", "mistral-common[opencv] (>=1.6.3)", "nltk (<=3.8.1)", "onnxconverter-common", "onnxconverter-common", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "pandas (<2.3.0)", "parameterized (>=0.9)", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic (>=2)", "pytest (>=7.2.0)", "pytest-asyncio", "pytest-order", "pytest-rerunfailures", "pytest-rich", "pytest-timeout", "pytest-xdist", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.11.2)", "ruff (==0.11.2)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorboard", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "tf2onnx", "timeout-decorator", "tokenizers (>=0.22.0,<=0.23.0)", "urllib3 (<2.0.0)"]
-dev-torch = ["GitPython (<3.1.19)", "GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.26.0)", "beautifulsoup4", "codecarbon (>=2.8.1)", "cookiecutter (==1.7.3)", "cookiecutter (==1.7.3)", "datasets (>=2.15.0)", "datasets (>=2.15.0)", "datasets (>=2.15.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "kenlm", "kernels (>=0.6.1,<=0.9)", "libcst", "libcst", "librosa", "mistral-common[opencv] (>=1.6.3)", "nltk (<=3.8.1)", "num2words", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "optuna", "pandas (<2.3.0)", "parameterized (>=0.9)", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic (>=2)", "pytest (>=7.2.0)", "pytest-asyncio", "pytest-order", "pytest-rerunfailures", "pytest-rich", "pytest-timeout", "pytest-xdist", "ray[tune] (>=2.7.0)", "rhoknp (>=1.1.0,<1.3.1)", "rich", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.11.2)", "ruff (==0.11.2)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict_core (>=20220729)", "sudachipy (>=0.6.6)", "tensorboard", "timeout-decorator", "timm (!=1.0.18,<=1.0.19)", "tokenizers (>=0.22.0,<=0.23.0)", "torch (>=2.2)", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic_lite (>=1.0.7)", "urllib3 (<2.0.0)"]
-flax = ["flax (>=0.4.1,<=0.7.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "optax (>=0.0.8,<=0.1.4)", "scipy (<1.13.0)"]
-flax-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"]
-ftfy = ["ftfy"]
-hf-xet = ["hf_xet"]
-hub-kernels = ["kernels (>=0.6.1,<=0.9)"]
-integrations = ["kernels (>=0.6.1,<=0.9)", "optuna", "ray[tune] (>=2.7.0)", "sigopt"]
-ja = ["fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "rhoknp (>=1.1.0,<1.3.1)", "sudachidict_core (>=20220729)", "sudachipy (>=0.6.6)", "unidic (>=1.0.2)", "unidic_lite (>=1.0.7)"]
-mistral-common = ["mistral-common[opencv] (>=1.6.3)"]
-modelcreation = ["cookiecutter (==1.7.3)"]
-natten = ["natten (>=0.14.6,<0.15.0)"]
-num2words = ["num2words"]
-onnx = ["onnxconverter-common", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "tf2onnx"]
-onnxruntime = ["onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)"]
-open-telemetry = ["opentelemetry-api", "opentelemetry-exporter-otlp", "opentelemetry-sdk"]
-optuna = ["optuna"]
-quality = ["GitPython (<3.1.19)", "datasets (>=2.15.0)", "libcst", "pandas (<2.3.0)", "rich", "ruff (==0.11.2)", "urllib3 (<2.0.0)"]
-ray = ["ray[tune] (>=2.7.0)"]
-retrieval = ["datasets (>=2.15.0)", "faiss-cpu"]
-ruff = ["ruff (==0.11.2)"]
-sagemaker = ["sagemaker (>=2.31.0)"]
-sentencepiece = ["protobuf", "sentencepiece (>=0.1.91,!=0.1.92)"]
-serving = ["accelerate (>=0.26.0)", "fastapi", "openai (>=1.98.0)", "pydantic (>=2)", "starlette", "torch (>=2.2)", "uvicorn"]
-sigopt = ["sigopt"]
-sklearn = ["scikit-learn"]
-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)", "torchaudio"]
-testing = ["GitPython (<3.1.19)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (>=2.15.0)", "datasets (>=2.15.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "libcst", "mistral-common[opencv] (>=1.6.3)", "nltk (<=3.8.1)", "parameterized (>=0.9)", "psutil", "pydantic (>=2)", "pytest (>=7.2.0)", "pytest-asyncio", "pytest-order", "pytest-rerunfailures", "pytest-rich", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.11.2)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorboard", "timeout-decorator"]
-tf = ["keras-nlp (>=0.3.1,<0.14.0)", "onnxconverter-common", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx"]
-tf-cpu = ["keras (>2.9,<2.16)", "keras-nlp (>=0.3.1,<0.14.0)", "onnxconverter-common", "tensorflow-cpu (>2.9,<2.16)", "tensorflow-probability (<0.24)", "tensorflow-text (<2.16)", "tf2onnx"]
-tf-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"]
-tiktoken = ["blobfile", "tiktoken"]
-timm = ["timm (!=1.0.18,<=1.0.19)"]
-tokenizers = ["tokenizers (>=0.22.0,<=0.23.0)"]
-torch = ["accelerate (>=0.26.0)", "torch (>=2.2)"]
-torch-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)", "torchaudio"]
-torch-vision = ["Pillow (>=10.0.1,<=15.0)", "torchvision"]
-torchhub = ["filelock", "huggingface-hub (>=0.34.0,<1.0)", "importlib_metadata", "numpy (>=1.17)", "packaging (>=20.0)", "protobuf", "regex (!=2019.12.17)", "requests", "sentencepiece (>=0.1.91,!=0.1.92)", "tokenizers (>=0.22.0,<=0.23.0)", "torch (>=2.2)", "tqdm (>=4.27)"]
-video = ["av"]
-vision = ["Pillow (>=10.0.1,<=15.0)"]
-
-[[package]]
-name = "triton"
-version = "3.3.1"
-description = "A language and compiler for custom Deep Learning operations"
-optional = false
-python-versions = "*"
-groups = ["main"]
-markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\" and python_version >= \"3.14\""
-files = [
- {file = "triton-3.3.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b74db445b1c562844d3cfad6e9679c72e93fdfb1a90a24052b03bb5c49d1242e"},
- {file = "triton-3.3.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b31e3aa26f8cb3cc5bf4e187bf737cbacf17311e1112b781d4a059353dfd731b"},
- {file = "triton-3.3.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9999e83aba21e1a78c1f36f21bce621b77bcaa530277a50484a7cb4a822f6e43"},
- {file = "triton-3.3.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b89d846b5a4198317fec27a5d3a609ea96b6d557ff44b56c23176546023c4240"},
- {file = "triton-3.3.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3198adb9d78b77818a5388bff89fa72ff36f9da0bc689db2f0a651a67ce6a42"},
- {file = "triton-3.3.1-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f6139aeb04a146b0b8e0fbbd89ad1e65861c57cfed881f21d62d3cb94a36bab7"},
-]
-
-[package.dependencies]
-setuptools = ">=40.8.0"
-
-[package.extras]
-build = ["cmake (>=3.20)", "lit"]
-tests = ["autopep8", "isort", "llnl-hatchet", "numpy", "pytest", "pytest-forked", "pytest-xdist", "scipy (>=1.7.1)"]
-tutorials = ["matplotlib", "pandas", "tabulate"]
-
-[[package]]
-name = "triton"
-version = "3.4.0"
-description = "A language and compiler for custom Deep Learning operations"
-optional = false
-python-versions = "<3.14,>=3.9"
-groups = ["main"]
-markers = "python_version < \"3.14\" and platform_machine == \"x86_64\" and platform_system == \"Linux\""
-files = [
- {file = "triton-3.4.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7ff2785de9bc02f500e085420273bb5cc9c9bb767584a4aa28d6e360cec70128"},
- {file = "triton-3.4.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7b70f5e6a41e52e48cfc087436c8a28c17ff98db369447bcaff3b887a3ab4467"},
- {file = "triton-3.4.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:31c1d84a5c0ec2c0f8e8a072d7fd150cab84a9c239eaddc6706c081bfae4eb04"},
- {file = "triton-3.4.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00be2964616f4c619193cb0d1b29a99bd4b001d7dc333816073f92cf2a8ccdeb"},
- {file = "triton-3.4.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7936b18a3499ed62059414d7df563e6c163c5e16c3773678a3ee3d417865035d"},
- {file = "triton-3.4.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:98e5c1442eaeabae2e2452ae765801bd53cd4ce873cab0d1bdd59a32ab2d9397"},
-]
-
-[package.dependencies]
-setuptools = ">=40.8.0"
-
-[package.extras]
-build = ["cmake (>=3.20,<4.0)", "lit"]
-tests = ["autopep8", "isort", "llnl-hatchet", "numpy", "pytest", "pytest-forked", "pytest-xdist", "scipy (>=1.7.1)"]
-tutorials = ["matplotlib", "pandas", "tabulate"]
-
-[[package]]
-name = "types-python-dateutil"
-version = "2.9.0.20250822"
-description = "Typing stubs for python-dateutil"
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "types_python_dateutil-2.9.0.20250822-py3-none-any.whl", hash = "sha256:849d52b737e10a6dc6621d2bd7940ec7c65fcb69e6aa2882acf4e56b2b508ddc"},
- {file = "types_python_dateutil-2.9.0.20250822.tar.gz", hash = "sha256:84c92c34bd8e68b117bff742bc00b692a1e8531262d4507b33afcc9f7716cd53"},
-]
-
-[[package]]
-name = "typing-extensions"
-version = "4.15.0"
-description = "Backported and Experimental Type Hints for Python 3.9+"
-optional = false
-python-versions = ">=3.9"
-groups = ["main", "dev"]
-files = [
- {file = "typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548"},
- {file = "typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466"},
-]
-
-[[package]]
-name = "typing-inspection"
-version = "0.4.1"
-description = "Runtime typing introspection tools"
-optional = false
-python-versions = ">=3.9"
-groups = ["main", "dev"]
-files = [
- {file = "typing_inspection-0.4.1-py3-none-any.whl", hash = "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51"},
- {file = "typing_inspection-0.4.1.tar.gz", hash = "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28"},
-]
-
-[package.dependencies]
-typing-extensions = ">=4.12.0"
-
-[[package]]
-name = "tzdata"
-version = "2025.2"
-description = "Provider of IANA time zone data"
-optional = false
-python-versions = ">=2"
-groups = ["dev"]
-files = [
- {file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"},
- {file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"},
-]
-
-[[package]]
-name = "uri-template"
-version = "1.3.0"
-description = "RFC 6570 URI Template Processor"
-optional = false
-python-versions = ">=3.7"
-groups = ["dev"]
-files = [
- {file = "uri-template-1.3.0.tar.gz", hash = "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7"},
- {file = "uri_template-1.3.0-py3-none-any.whl", hash = "sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363"},
-]
-
-[package.extras]
-dev = ["flake8", "flake8-annotations", "flake8-bandit", "flake8-bugbear", "flake8-commas", "flake8-comprehensions", "flake8-continuation", "flake8-datetimez", "flake8-docstrings", "flake8-import-order", "flake8-literal", "flake8-modern-annotations", "flake8-noqa", "flake8-pyproject", "flake8-requirements", "flake8-typechecking-import", "flake8-use-fstring", "mypy", "pep8-naming", "types-PyYAML"]
-
-[[package]]
-name = "urllib3"
-version = "2.5.0"
-description = "HTTP library with thread-safe connection pooling, file post, and more."
-optional = false
-python-versions = ">=3.9"
-groups = ["main", "dev", "xla"]
-files = [
- {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"},
- {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"},
-]
-
-[package.extras]
-brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""]
-h2 = ["h2 (>=4,<5)"]
-socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
-zstd = ["zstandard (>=0.18.0)"]
-
-[[package]]
-name = "virtualenv"
-version = "20.34.0"
-description = "Virtual Python Environment builder"
-optional = false
-python-versions = ">=3.8"
-groups = ["main"]
-files = [
- {file = "virtualenv-20.34.0-py3-none-any.whl", hash = "sha256:341f5afa7eee943e4984a9207c025feedd768baff6753cd660c857ceb3e36026"},
- {file = "virtualenv-20.34.0.tar.gz", hash = "sha256:44815b2c9dee7ed86e387b842a84f20b93f7f417f95886ca1996a72a4138eb1a"},
-]
-
-[package.dependencies]
-distlib = ">=0.3.7,<1"
-filelock = ">=3.12.2,<4"
-platformdirs = ">=3.9.1,<5"
-typing-extensions = {version = ">=4.13.2", markers = "python_version < \"3.11\""}
-
-[package.extras]
-docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"]
-test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8) ; platform_python_implementation == \"PyPy\" or platform_python_implementation == \"GraalVM\" or platform_python_implementation == \"CPython\" and sys_platform == \"win32\" and python_version >= \"3.13\"", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10) ; platform_python_implementation == \"CPython\""]
-
-[[package]]
-name = "watchdog"
-version = "6.0.0"
-description = "Filesystem events monitoring"
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-markers = "platform_system != \"Darwin\""
-files = [
- {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26"},
- {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112"},
- {file = "watchdog-6.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3"},
- {file = "watchdog-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c"},
- {file = "watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2"},
- {file = "watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c"},
- {file = "watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948"},
- {file = "watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860"},
- {file = "watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0"},
- {file = "watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c"},
- {file = "watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134"},
- {file = "watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b"},
- {file = "watchdog-6.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e6f0e77c9417e7cd62af82529b10563db3423625c5fce018430b249bf977f9e8"},
- {file = "watchdog-6.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:90c8e78f3b94014f7aaae121e6b909674df5b46ec24d6bebc45c44c56729af2a"},
- {file = "watchdog-6.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c"},
- {file = "watchdog-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881"},
- {file = "watchdog-6.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11"},
- {file = "watchdog-6.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7a0e56874cfbc4b9b05c60c8a1926fedf56324bb08cfbc188969777940aef3aa"},
- {file = "watchdog-6.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e6439e374fc012255b4ec786ae3c4bc838cd7309a540e5fe0952d03687d8804e"},
- {file = "watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13"},
- {file = "watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379"},
- {file = "watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e"},
- {file = "watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f"},
- {file = "watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26"},
- {file = "watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c"},
- {file = "watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2"},
- {file = "watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a"},
- {file = "watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680"},
- {file = "watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f"},
- {file = "watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282"},
-]
-
-[package.extras]
-watchmedo = ["PyYAML (>=3.10)"]
-
-[[package]]
-name = "wcwidth"
-version = "0.2.13"
-description = "Measures the displayed width of unicode strings in a terminal"
-optional = false
-python-versions = "*"
-groups = ["dev"]
-files = [
- {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"},
- {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"},
-]
-
-[[package]]
-name = "webcolors"
-version = "24.11.1"
-description = "A library for working with the color formats defined by HTML and CSS."
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "webcolors-24.11.1-py3-none-any.whl", hash = "sha256:515291393b4cdf0eb19c155749a096f779f7d909f7cceea072791cb9095b92e9"},
- {file = "webcolors-24.11.1.tar.gz", hash = "sha256:ecb3d768f32202af770477b8b65f318fa4f566c22948673a977b00d589dd80f6"},
-]
-
-[[package]]
-name = "webencodings"
-version = "0.5.1"
-description = "Character encoding aliases for legacy web content"
-optional = false
-python-versions = "*"
-groups = ["dev"]
-files = [
- {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"},
- {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"},
-]
-
-[[package]]
-name = "websocket-client"
-version = "1.8.0"
-description = "WebSocket client for Python with low level API options"
-optional = false
-python-versions = ">=3.8"
-groups = ["dev"]
-files = [
- {file = "websocket_client-1.8.0-py3-none-any.whl", hash = "sha256:17b44cc997f5c498e809b22cdf2d9c7a9e71c02c8cc2b6c56e7c2d1239bfa526"},
- {file = "websocket_client-1.8.0.tar.gz", hash = "sha256:3239df9f44da632f96012472805d40a23281a991027ce11d2f45a6f24ac4c3da"},
-]
-
-[package.extras]
-docs = ["Sphinx (>=6.0)", "myst-parser (>=2.0.0)", "sphinx-rtd-theme (>=1.1.0)"]
-optional = ["python-socks", "wsaccel"]
-test = ["websockets"]
-
-[[package]]
-name = "widgetsnbextension"
-version = "4.0.14"
-description = "Jupyter interactive widgets for Jupyter Notebook"
-optional = false
-python-versions = ">=3.7"
-groups = ["dev"]
-files = [
- {file = "widgetsnbextension-4.0.14-py3-none-any.whl", hash = "sha256:4875a9eaf72fbf5079dc372a51a9f268fc38d46f767cbf85c43a36da5cb9b575"},
- {file = "widgetsnbextension-4.0.14.tar.gz", hash = "sha256:a3629b04e3edb893212df862038c7232f62973373869db5084aed739b437b5af"},
-]
-
-[[package]]
-name = "xxhash"
-version = "3.5.0"
-description = "Python binding for xxHash"
-optional = false
-python-versions = ">=3.7"
-groups = ["dev"]
-files = [
- {file = "xxhash-3.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ece616532c499ee9afbb83078b1b952beffef121d989841f7f4b3dc5ac0fd212"},
- {file = "xxhash-3.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3171f693dbc2cef6477054a665dc255d996646b4023fe56cb4db80e26f4cc520"},
- {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c5d3e570ef46adaf93fc81b44aca6002b5a4d8ca11bd0580c07eac537f36680"},
- {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7cb29a034301e2982df8b1fe6328a84f4b676106a13e9135a0d7e0c3e9f806da"},
- {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5d0d307d27099bb0cbeea7260eb39ed4fdb99c5542e21e94bb6fd29e49c57a23"},
- {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0342aafd421795d740e514bc9858ebddfc705a75a8c5046ac56d85fe97bf196"},
- {file = "xxhash-3.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3dbbd9892c5ebffeca1ed620cf0ade13eb55a0d8c84e0751a6653adc6ac40d0c"},
- {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4cc2d67fdb4d057730c75a64c5923abfa17775ae234a71b0200346bfb0a7f482"},
- {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:ec28adb204b759306a3d64358a5e5c07d7b1dd0ccbce04aa76cb9377b7b70296"},
- {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1328f6d8cca2b86acb14104e381225a3d7b42c92c4b86ceae814e5c400dbb415"},
- {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8d47ebd9f5d9607fd039c1fbf4994e3b071ea23eff42f4ecef246ab2b7334198"},
- {file = "xxhash-3.5.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b96d559e0fcddd3343c510a0fe2b127fbff16bf346dd76280b82292567523442"},
- {file = "xxhash-3.5.0-cp310-cp310-win32.whl", hash = "sha256:61c722ed8d49ac9bc26c7071eeaa1f6ff24053d553146d5df031802deffd03da"},
- {file = "xxhash-3.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:9bed5144c6923cc902cd14bb8963f2d5e034def4486ab0bbe1f58f03f042f9a9"},
- {file = "xxhash-3.5.0-cp310-cp310-win_arm64.whl", hash = "sha256:893074d651cf25c1cc14e3bea4fceefd67f2921b1bb8e40fcfeba56820de80c6"},
- {file = "xxhash-3.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:02c2e816896dc6f85922ced60097bcf6f008dedfc5073dcba32f9c8dd786f3c1"},
- {file = "xxhash-3.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6027dcd885e21581e46d3c7f682cfb2b870942feeed58a21c29583512c3f09f8"},
- {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1308fa542bbdbf2fa85e9e66b1077eea3a88bef38ee8a06270b4298a7a62a166"},
- {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c28b2fdcee797e1c1961cd3bcd3d545cab22ad202c846235197935e1df2f8ef7"},
- {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:924361811732ddad75ff23e90efd9ccfda4f664132feecb90895bade6a1b4623"},
- {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89997aa1c4b6a5b1e5b588979d1da048a3c6f15e55c11d117a56b75c84531f5a"},
- {file = "xxhash-3.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:685c4f4e8c59837de103344eb1c8a3851f670309eb5c361f746805c5471b8c88"},
- {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbd2ecfbfee70bc1a4acb7461fa6af7748ec2ab08ac0fa298f281c51518f982c"},
- {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:25b5a51dc3dfb20a10833c8eee25903fd2e14059e9afcd329c9da20609a307b2"},
- {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:a8fb786fb754ef6ff8c120cb96629fb518f8eb5a61a16aac3a979a9dbd40a084"},
- {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a905ad00ad1e1c34fe4e9d7c1d949ab09c6fa90c919860c1534ff479f40fd12d"},
- {file = "xxhash-3.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:963be41bcd49f53af6d795f65c0da9b4cc518c0dd9c47145c98f61cb464f4839"},
- {file = "xxhash-3.5.0-cp311-cp311-win32.whl", hash = "sha256:109b436096d0a2dd039c355fa3414160ec4d843dfecc64a14077332a00aeb7da"},
- {file = "xxhash-3.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:b702f806693201ad6c0a05ddbbe4c8f359626d0b3305f766077d51388a6bac58"},
- {file = "xxhash-3.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:c4dcb4120d0cc3cc448624147dba64e9021b278c63e34a38789b688fd0da9bf3"},
- {file = "xxhash-3.5.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:14470ace8bd3b5d51318782cd94e6f94431974f16cb3b8dc15d52f3b69df8e00"},
- {file = "xxhash-3.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:59aa1203de1cb96dbeab595ded0ad0c0056bb2245ae11fac11c0ceea861382b9"},
- {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08424f6648526076e28fae6ea2806c0a7d504b9ef05ae61d196d571e5c879c84"},
- {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:61a1ff00674879725b194695e17f23d3248998b843eb5e933007ca743310f793"},
- {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f2f2c61bee5844d41c3eb015ac652a0229e901074951ae48581d58bfb2ba01be"},
- {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d32a592cac88d18cc09a89172e1c32d7f2a6e516c3dfde1b9adb90ab5df54a6"},
- {file = "xxhash-3.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:70dabf941dede727cca579e8c205e61121afc9b28516752fd65724be1355cc90"},
- {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e5d0ddaca65ecca9c10dcf01730165fd858533d0be84c75c327487c37a906a27"},
- {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:3e5b5e16c5a480fe5f59f56c30abdeba09ffd75da8d13f6b9b6fd224d0b4d0a2"},
- {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:149b7914451eb154b3dfaa721315117ea1dac2cc55a01bfbd4df7c68c5dd683d"},
- {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:eade977f5c96c677035ff39c56ac74d851b1cca7d607ab3d8f23c6b859379cab"},
- {file = "xxhash-3.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fa9f547bd98f5553d03160967866a71056a60960be00356a15ecc44efb40ba8e"},
- {file = "xxhash-3.5.0-cp312-cp312-win32.whl", hash = "sha256:f7b58d1fd3551b8c80a971199543379be1cee3d0d409e1f6d8b01c1a2eebf1f8"},
- {file = "xxhash-3.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:fa0cafd3a2af231b4e113fba24a65d7922af91aeb23774a8b78228e6cd785e3e"},
- {file = "xxhash-3.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:586886c7e89cb9828bcd8a5686b12e161368e0064d040e225e72607b43858ba2"},
- {file = "xxhash-3.5.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:37889a0d13b0b7d739cfc128b1c902f04e32de17b33d74b637ad42f1c55101f6"},
- {file = "xxhash-3.5.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:97a662338797c660178e682f3bc180277b9569a59abfb5925e8620fba00b9fc5"},
- {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f85e0108d51092bdda90672476c7d909c04ada6923c14ff9d913c4f7dc8a3bc"},
- {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd2fd827b0ba763ac919440042302315c564fdb797294d86e8cdd4578e3bc7f3"},
- {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:82085c2abec437abebf457c1d12fccb30cc8b3774a0814872511f0f0562c768c"},
- {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07fda5de378626e502b42b311b049848c2ef38784d0d67b6f30bb5008642f8eb"},
- {file = "xxhash-3.5.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c279f0d2b34ef15f922b77966640ade58b4ccdfef1c4d94b20f2a364617a493f"},
- {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:89e66ceed67b213dec5a773e2f7a9e8c58f64daeb38c7859d8815d2c89f39ad7"},
- {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:bcd51708a633410737111e998ceb3b45d3dbc98c0931f743d9bb0a209033a326"},
- {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3ff2c0a34eae7df88c868be53a8dd56fbdf592109e21d4bfa092a27b0bf4a7bf"},
- {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4e28503dccc7d32e0b9817aa0cbfc1f45f563b2c995b7a66c4c8a0d232e840c7"},
- {file = "xxhash-3.5.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a6c50017518329ed65a9e4829154626f008916d36295b6a3ba336e2458824c8c"},
- {file = "xxhash-3.5.0-cp313-cp313-win32.whl", hash = "sha256:53a068fe70301ec30d868ece566ac90d873e3bb059cf83c32e76012c889b8637"},
- {file = "xxhash-3.5.0-cp313-cp313-win_amd64.whl", hash = "sha256:80babcc30e7a1a484eab952d76a4f4673ff601f54d5142c26826502740e70b43"},
- {file = "xxhash-3.5.0-cp313-cp313-win_arm64.whl", hash = "sha256:4811336f1ce11cac89dcbd18f3a25c527c16311709a89313c3acaf771def2d4b"},
- {file = "xxhash-3.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6e5f70f6dca1d3b09bccb7daf4e087075ff776e3da9ac870f86ca316736bb4aa"},
- {file = "xxhash-3.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e76e83efc7b443052dd1e585a76201e40b3411fe3da7af4fe434ec51b2f163b"},
- {file = "xxhash-3.5.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:33eac61d0796ca0591f94548dcfe37bb193671e0c9bcf065789b5792f2eda644"},
- {file = "xxhash-3.5.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ec70a89be933ea49222fafc3999987d7899fc676f688dd12252509434636622"},
- {file = "xxhash-3.5.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86b8e7f703ec6ff4f351cfdb9f428955859537125904aa8c963604f2e9d3e7"},
- {file = "xxhash-3.5.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0adfbd36003d9f86c8c97110039f7539b379f28656a04097e7434d3eaf9aa131"},
- {file = "xxhash-3.5.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:63107013578c8a730419adc05608756c3fa640bdc6abe806c3123a49fb829f43"},
- {file = "xxhash-3.5.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:683b94dbd1ca67557850b86423318a2e323511648f9f3f7b1840408a02b9a48c"},
- {file = "xxhash-3.5.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:5d2a01dcce81789cf4b12d478b5464632204f4c834dc2d064902ee27d2d1f0ee"},
- {file = "xxhash-3.5.0-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:a9d360a792cbcce2fe7b66b8d51274ec297c53cbc423401480e53b26161a290d"},
- {file = "xxhash-3.5.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:f0b48edbebea1b7421a9c687c304f7b44d0677c46498a046079d445454504737"},
- {file = "xxhash-3.5.0-cp37-cp37m-win32.whl", hash = "sha256:7ccb800c9418e438b44b060a32adeb8393764da7441eb52aa2aa195448935306"},
- {file = "xxhash-3.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c3bc7bf8cb8806f8d1c9bf149c18708cb1c406520097d6b0a73977460ea03602"},
- {file = "xxhash-3.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:74752ecaa544657d88b1d1c94ae68031e364a4d47005a90288f3bab3da3c970f"},
- {file = "xxhash-3.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dee1316133c9b463aa81aca676bc506d3f80d8f65aeb0bba2b78d0b30c51d7bd"},
- {file = "xxhash-3.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:602d339548d35a8579c6b013339fb34aee2df9b4e105f985443d2860e4d7ffaa"},
- {file = "xxhash-3.5.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:695735deeddfb35da1677dbc16a083445360e37ff46d8ac5c6fcd64917ff9ade"},
- {file = "xxhash-3.5.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1030a39ba01b0c519b1a82f80e8802630d16ab95dc3f2b2386a0b5c8ed5cbb10"},
- {file = "xxhash-3.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5bc08f33c4966f4eb6590d6ff3ceae76151ad744576b5fc6c4ba8edd459fdec"},
- {file = "xxhash-3.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:160e0c19ee500482ddfb5d5570a0415f565d8ae2b3fd69c5dcfce8a58107b1c3"},
- {file = "xxhash-3.5.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f1abffa122452481a61c3551ab3c89d72238e279e517705b8b03847b1d93d738"},
- {file = "xxhash-3.5.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:d5e9db7ef3ecbfc0b4733579cea45713a76852b002cf605420b12ef3ef1ec148"},
- {file = "xxhash-3.5.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:23241ff6423378a731d84864bf923a41649dc67b144debd1077f02e6249a0d54"},
- {file = "xxhash-3.5.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:82b833d5563fefd6fceafb1aed2f3f3ebe19f84760fdd289f8b926731c2e6e91"},
- {file = "xxhash-3.5.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0a80ad0ffd78bef9509eee27b4a29e56f5414b87fb01a888353e3d5bda7038bd"},
- {file = "xxhash-3.5.0-cp38-cp38-win32.whl", hash = "sha256:50ac2184ffb1b999e11e27c7e3e70cc1139047e7ebc1aa95ed12f4269abe98d4"},
- {file = "xxhash-3.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:392f52ebbb932db566973693de48f15ce787cabd15cf6334e855ed22ea0be5b3"},
- {file = "xxhash-3.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bfc8cdd7f33d57f0468b0614ae634cc38ab9202c6957a60e31d285a71ebe0301"},
- {file = "xxhash-3.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e0c48b6300cd0b0106bf49169c3e0536408dfbeb1ccb53180068a18b03c662ab"},
- {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe1a92cfbaa0a1253e339ccec42dbe6db262615e52df591b68726ab10338003f"},
- {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:33513d6cc3ed3b559134fb307aae9bdd94d7e7c02907b37896a6c45ff9ce51bd"},
- {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eefc37f6138f522e771ac6db71a6d4838ec7933939676f3753eafd7d3f4c40bc"},
- {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a606c8070ada8aa2a88e181773fa1ef17ba65ce5dd168b9d08038e2a61b33754"},
- {file = "xxhash-3.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:42eca420c8fa072cc1dd62597635d140e78e384a79bb4944f825fbef8bfeeef6"},
- {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:604253b2143e13218ff1ef0b59ce67f18b8bd1c4205d2ffda22b09b426386898"},
- {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6e93a5ad22f434d7876665444a97e713a8f60b5b1a3521e8df11b98309bff833"},
- {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:7a46e1d6d2817ba8024de44c4fd79913a90e5f7265434cef97026215b7d30df6"},
- {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:30eb2efe6503c379b7ab99c81ba4a779748e3830241f032ab46bd182bf5873af"},
- {file = "xxhash-3.5.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c8aa771ff2c13dd9cda8166d685d7333d389fae30a4d2bb39d63ab5775de8606"},
- {file = "xxhash-3.5.0-cp39-cp39-win32.whl", hash = "sha256:5ed9ebc46f24cf91034544b26b131241b699edbfc99ec5e7f8f3d02d6eb7fba4"},
- {file = "xxhash-3.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:220f3f896c6b8d0316f63f16c077d52c412619e475f9372333474ee15133a558"},
- {file = "xxhash-3.5.0-cp39-cp39-win_arm64.whl", hash = "sha256:a7b1d8315d9b5e9f89eb2933b73afae6ec9597a258d52190944437158b49d38e"},
- {file = "xxhash-3.5.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2014c5b3ff15e64feecb6b713af12093f75b7926049e26a580e94dcad3c73d8c"},
- {file = "xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fab81ef75003eda96239a23eda4e4543cedc22e34c373edcaf744e721a163986"},
- {file = "xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e2febf914ace002132aa09169cc572e0d8959d0f305f93d5828c4836f9bc5a6"},
- {file = "xxhash-3.5.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5d3a10609c51da2a1c0ea0293fc3968ca0a18bd73838455b5bca3069d7f8e32b"},
- {file = "xxhash-3.5.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5a74f23335b9689b66eb6dbe2a931a88fcd7a4c2cc4b1cb0edba8ce381c7a1da"},
- {file = "xxhash-3.5.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2b4154c00eb22e4d543f472cfca430e7962a0f1d0f3778334f2e08a7ba59363c"},
- {file = "xxhash-3.5.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d30bbc1644f726b825b3278764240f449d75f1a8bdda892e641d4a688b1494ae"},
- {file = "xxhash-3.5.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fa0b72f2423e2aa53077e54a61c28e181d23effeaafd73fcb9c494e60930c8e"},
- {file = "xxhash-3.5.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:13de2b76c1835399b2e419a296d5b38dc4855385d9e96916299170085ef72f57"},
- {file = "xxhash-3.5.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:0691bfcc4f9c656bcb96cc5db94b4d75980b9d5589f2e59de790091028580837"},
- {file = "xxhash-3.5.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:297595fe6138d4da2c8ce9e72a04d73e58725bb60f3a19048bc96ab2ff31c692"},
- {file = "xxhash-3.5.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc1276d369452040cbb943300dc8abeedab14245ea44056a2943183822513a18"},
- {file = "xxhash-3.5.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2061188a1ba352fc699c82bff722f4baacb4b4b8b2f0c745d2001e56d0dfb514"},
- {file = "xxhash-3.5.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38c384c434021e4f62b8d9ba0bc9467e14d394893077e2c66d826243025e1f81"},
- {file = "xxhash-3.5.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e6a4dd644d72ab316b580a1c120b375890e4c52ec392d4aef3c63361ec4d77d1"},
- {file = "xxhash-3.5.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:531af8845aaadcadf951b7e0c1345c6b9c68a990eeb74ff9acd8501a0ad6a1c9"},
- {file = "xxhash-3.5.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ce379bcaa9fcc00f19affa7773084dd09f5b59947b3fb47a1ceb0179f91aaa1"},
- {file = "xxhash-3.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd1b2281d01723f076df3c8188f43f2472248a6b63118b036e641243656b1b0f"},
- {file = "xxhash-3.5.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c770750cc80e8694492244bca7251385188bc5597b6a39d98a9f30e8da984e0"},
- {file = "xxhash-3.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b150b8467852e1bd844387459aa6fbe11d7f38b56e901f9f3b3e6aba0d660240"},
- {file = "xxhash-3.5.0.tar.gz", hash = "sha256:84f2caddf951c9cbf8dc2e22a89d4ccf5d86391ac6418fe81e3c67d0cf60b45f"},
-]
-
-[[package]]
-name = "yarl"
-version = "1.20.1"
-description = "Yet another URL library"
-optional = false
-python-versions = ">=3.9"
-groups = ["dev"]
-files = [
- {file = "yarl-1.20.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6032e6da6abd41e4acda34d75a816012717000fa6839f37124a47fcefc49bec4"},
- {file = "yarl-1.20.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2c7b34d804b8cf9b214f05015c4fee2ebe7ed05cf581e7192c06555c71f4446a"},
- {file = "yarl-1.20.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0c869f2651cc77465f6cd01d938d91a11d9ea5d798738c1dc077f3de0b5e5fed"},
- {file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62915e6688eb4d180d93840cda4110995ad50c459bf931b8b3775b37c264af1e"},
- {file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:41ebd28167bc6af8abb97fec1a399f412eec5fd61a3ccbe2305a18b84fb4ca73"},
- {file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21242b4288a6d56f04ea193adde174b7e347ac46ce6bc84989ff7c1b1ecea84e"},
- {file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bea21cdae6c7eb02ba02a475f37463abfe0a01f5d7200121b03e605d6a0439f8"},
- {file = "yarl-1.20.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f8a891e4a22a89f5dde7862994485e19db246b70bb288d3ce73a34422e55b23"},
- {file = "yarl-1.20.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dd803820d44c8853a109a34e3660e5a61beae12970da479cf44aa2954019bf70"},
- {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b982fa7f74c80d5c0c7b5b38f908971e513380a10fecea528091405f519b9ebb"},
- {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:33f29ecfe0330c570d997bcf1afd304377f2e48f61447f37e846a6058a4d33b2"},
- {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:835ab2cfc74d5eb4a6a528c57f05688099da41cf4957cf08cad38647e4a83b30"},
- {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:46b5e0ccf1943a9a6e766b2c2b8c732c55b34e28be57d8daa2b3c1d1d4009309"},
- {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:df47c55f7d74127d1b11251fe6397d84afdde0d53b90bedb46a23c0e534f9d24"},
- {file = "yarl-1.20.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:76d12524d05841276b0e22573f28d5fbcb67589836772ae9244d90dd7d66aa13"},
- {file = "yarl-1.20.1-cp310-cp310-win32.whl", hash = "sha256:6c4fbf6b02d70e512d7ade4b1f998f237137f1417ab07ec06358ea04f69134f8"},
- {file = "yarl-1.20.1-cp310-cp310-win_amd64.whl", hash = "sha256:aef6c4d69554d44b7f9d923245f8ad9a707d971e6209d51279196d8e8fe1ae16"},
- {file = "yarl-1.20.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:47ee6188fea634bdfaeb2cc420f5b3b17332e6225ce88149a17c413c77ff269e"},
- {file = "yarl-1.20.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d0f6500f69e8402d513e5eedb77a4e1818691e8f45e6b687147963514d84b44b"},
- {file = "yarl-1.20.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a8900a42fcdaad568de58887c7b2f602962356908eedb7628eaf6021a6e435b"},
- {file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bad6d131fda8ef508b36be3ece16d0902e80b88ea7200f030a0f6c11d9e508d4"},
- {file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:df018d92fe22aaebb679a7f89fe0c0f368ec497e3dda6cb81a567610f04501f1"},
- {file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f969afbb0a9b63c18d0feecf0db09d164b7a44a053e78a7d05f5df163e43833"},
- {file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:812303eb4aa98e302886ccda58d6b099e3576b1b9276161469c25803a8db277d"},
- {file = "yarl-1.20.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98c4a7d166635147924aa0bf9bfe8d8abad6fffa6102de9c99ea04a1376f91e8"},
- {file = "yarl-1.20.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12e768f966538e81e6e7550f9086a6236b16e26cd964cf4df35349970f3551cf"},
- {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fe41919b9d899661c5c28a8b4b0acf704510b88f27f0934ac7a7bebdd8938d5e"},
- {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:8601bc010d1d7780592f3fc1bdc6c72e2b6466ea34569778422943e1a1f3c389"},
- {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:daadbdc1f2a9033a2399c42646fbd46da7992e868a5fe9513860122d7fe7a73f"},
- {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:03aa1e041727cb438ca762628109ef1333498b122e4c76dd858d186a37cec845"},
- {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:642980ef5e0fa1de5fa96d905c7e00cb2c47cb468bfcac5a18c58e27dbf8d8d1"},
- {file = "yarl-1.20.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:86971e2795584fe8c002356d3b97ef6c61862720eeff03db2a7c86b678d85b3e"},
- {file = "yarl-1.20.1-cp311-cp311-win32.whl", hash = "sha256:597f40615b8d25812f14562699e287f0dcc035d25eb74da72cae043bb884d773"},
- {file = "yarl-1.20.1-cp311-cp311-win_amd64.whl", hash = "sha256:26ef53a9e726e61e9cd1cda6b478f17e350fb5800b4bd1cd9fe81c4d91cfeb2e"},
- {file = "yarl-1.20.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdcc4cd244e58593a4379fe60fdee5ac0331f8eb70320a24d591a3be197b94a9"},
- {file = "yarl-1.20.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b29a2c385a5f5b9c7d9347e5812b6f7ab267193c62d282a540b4fc528c8a9d2a"},
- {file = "yarl-1.20.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1112ae8154186dfe2de4732197f59c05a83dc814849a5ced892b708033f40dc2"},
- {file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:90bbd29c4fe234233f7fa2b9b121fb63c321830e5d05b45153a2ca68f7d310ee"},
- {file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:680e19c7ce3710ac4cd964e90dad99bf9b5029372ba0c7cbfcd55e54d90ea819"},
- {file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a979218c1fdb4246a05efc2cc23859d47c89af463a90b99b7c56094daf25a16"},
- {file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255b468adf57b4a7b65d8aad5b5138dce6a0752c139965711bdcb81bc370e1b6"},
- {file = "yarl-1.20.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a97d67108e79cfe22e2b430d80d7571ae57d19f17cda8bb967057ca8a7bf5bfd"},
- {file = "yarl-1.20.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8570d998db4ddbfb9a590b185a0a33dbf8aafb831d07a5257b4ec9948df9cb0a"},
- {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:97c75596019baae7c71ccf1d8cc4738bc08134060d0adfcbe5642f778d1dca38"},
- {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:1c48912653e63aef91ff988c5432832692ac5a1d8f0fb8a33091520b5bbe19ef"},
- {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4c3ae28f3ae1563c50f3d37f064ddb1511ecc1d5584e88c6b7c63cf7702a6d5f"},
- {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c5e9642f27036283550f5f57dc6156c51084b458570b9d0d96100c8bebb186a8"},
- {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:2c26b0c49220d5799f7b22c6838409ee9bc58ee5c95361a4d7831f03cc225b5a"},
- {file = "yarl-1.20.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:564ab3d517e3d01c408c67f2e5247aad4019dcf1969982aba3974b4093279004"},
- {file = "yarl-1.20.1-cp312-cp312-win32.whl", hash = "sha256:daea0d313868da1cf2fac6b2d3a25c6e3a9e879483244be38c8e6a41f1d876a5"},
- {file = "yarl-1.20.1-cp312-cp312-win_amd64.whl", hash = "sha256:48ea7d7f9be0487339828a4de0360d7ce0efc06524a48e1810f945c45b813698"},
- {file = "yarl-1.20.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:0b5ff0fbb7c9f1b1b5ab53330acbfc5247893069e7716840c8e7d5bb7355038a"},
- {file = "yarl-1.20.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:14f326acd845c2b2e2eb38fb1346c94f7f3b01a4f5c788f8144f9b630bfff9a3"},
- {file = "yarl-1.20.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f60e4ad5db23f0b96e49c018596707c3ae89f5d0bd97f0ad3684bcbad899f1e7"},
- {file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49bdd1b8e00ce57e68ba51916e4bb04461746e794e7c4d4bbc42ba2f18297691"},
- {file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:66252d780b45189975abfed839616e8fd2dbacbdc262105ad7742c6ae58f3e31"},
- {file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59174e7332f5d153d8f7452a102b103e2e74035ad085f404df2e40e663a22b28"},
- {file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e3968ec7d92a0c0f9ac34d5ecfd03869ec0cab0697c91a45db3fbbd95fe1b653"},
- {file = "yarl-1.20.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1a4fbb50e14396ba3d375f68bfe02215d8e7bc3ec49da8341fe3157f59d2ff5"},
- {file = "yarl-1.20.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11a62c839c3a8eac2410e951301309426f368388ff2f33799052787035793b02"},
- {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:041eaa14f73ff5a8986b4388ac6bb43a77f2ea09bf1913df7a35d4646db69e53"},
- {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:377fae2fef158e8fd9d60b4c8751387b8d1fb121d3d0b8e9b0be07d1b41e83dc"},
- {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:1c92f4390e407513f619d49319023664643d3339bd5e5a56a3bebe01bc67ec04"},
- {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d25ddcf954df1754ab0f86bb696af765c5bfaba39b74095f27eececa049ef9a4"},
- {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:909313577e9619dcff8c31a0ea2aa0a2a828341d92673015456b3ae492e7317b"},
- {file = "yarl-1.20.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:793fd0580cb9664548c6b83c63b43c477212c0260891ddf86809e1c06c8b08f1"},
- {file = "yarl-1.20.1-cp313-cp313-win32.whl", hash = "sha256:468f6e40285de5a5b3c44981ca3a319a4b208ccc07d526b20b12aeedcfa654b7"},
- {file = "yarl-1.20.1-cp313-cp313-win_amd64.whl", hash = "sha256:495b4ef2fea40596bfc0affe3837411d6aa3371abcf31aac0ccc4bdd64d4ef5c"},
- {file = "yarl-1.20.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:f60233b98423aab21d249a30eb27c389c14929f47be8430efa7dbd91493a729d"},
- {file = "yarl-1.20.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6f3eff4cc3f03d650d8755c6eefc844edde99d641d0dcf4da3ab27141a5f8ddf"},
- {file = "yarl-1.20.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:69ff8439d8ba832d6bed88af2c2b3445977eba9a4588b787b32945871c2444e3"},
- {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cf34efa60eb81dd2645a2e13e00bb98b76c35ab5061a3989c7a70f78c85006d"},
- {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8e0fe9364ad0fddab2688ce72cb7a8e61ea42eff3c7caeeb83874a5d479c896c"},
- {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f64fbf81878ba914562c672024089e3401974a39767747691c65080a67b18c1"},
- {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6342d643bf9a1de97e512e45e4b9560a043347e779a173250824f8b254bd5ce"},
- {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56dac5f452ed25eef0f6e3c6a066c6ab68971d96a9fb441791cad0efba6140d3"},
- {file = "yarl-1.20.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7d7f497126d65e2cad8dc5f97d34c27b19199b6414a40cb36b52f41b79014be"},
- {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:67e708dfb8e78d8a19169818eeb5c7a80717562de9051bf2413aca8e3696bf16"},
- {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:595c07bc79af2494365cc96ddeb772f76272364ef7c80fb892ef9d0649586513"},
- {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7bdd2f80f4a7df852ab9ab49484a4dee8030023aa536df41f2d922fd57bf023f"},
- {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c03bfebc4ae8d862f853a9757199677ab74ec25424d0ebd68a0027e9c639a390"},
- {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:344d1103e9c1523f32a5ed704d576172d2cabed3122ea90b1d4e11fe17c66458"},
- {file = "yarl-1.20.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:88cab98aa4e13e1ade8c141daeedd300a4603b7132819c484841bb7af3edce9e"},
- {file = "yarl-1.20.1-cp313-cp313t-win32.whl", hash = "sha256:b121ff6a7cbd4abc28985b6028235491941b9fe8fe226e6fdc539c977ea1739d"},
- {file = "yarl-1.20.1-cp313-cp313t-win_amd64.whl", hash = "sha256:541d050a355bbbc27e55d906bc91cb6fe42f96c01413dd0f4ed5a5240513874f"},
- {file = "yarl-1.20.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e42ba79e2efb6845ebab49c7bf20306c4edf74a0b20fc6b2ccdd1a219d12fad3"},
- {file = "yarl-1.20.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:41493b9b7c312ac448b7f0a42a089dffe1d6e6e981a2d76205801a023ed26a2b"},
- {file = "yarl-1.20.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f5a5928ff5eb13408c62a968ac90d43f8322fd56d87008b8f9dabf3c0f6ee983"},
- {file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30c41ad5d717b3961b2dd785593b67d386b73feca30522048d37298fee981805"},
- {file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:59febc3969b0781682b469d4aca1a5cab7505a4f7b85acf6db01fa500fa3f6ba"},
- {file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d2b6fb3622b7e5bf7a6e5b679a69326b4279e805ed1699d749739a61d242449e"},
- {file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:749d73611db8d26a6281086f859ea7ec08f9c4c56cec864e52028c8b328db723"},
- {file = "yarl-1.20.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9427925776096e664c39e131447aa20ec738bdd77c049c48ea5200db2237e000"},
- {file = "yarl-1.20.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff70f32aa316393eaf8222d518ce9118148eddb8a53073c2403863b41033eed5"},
- {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:c7ddf7a09f38667aea38801da8b8d6bfe81df767d9dfc8c88eb45827b195cd1c"},
- {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:57edc88517d7fc62b174fcfb2e939fbc486a68315d648d7e74d07fac42cec240"},
- {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:dab096ce479d5894d62c26ff4f699ec9072269d514b4edd630a393223f45a0ee"},
- {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:14a85f3bd2d7bb255be7183e5d7d6e70add151a98edf56a770d6140f5d5f4010"},
- {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2c89b5c792685dd9cd3fa9761c1b9f46fc240c2a3265483acc1565769996a3f8"},
- {file = "yarl-1.20.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:69e9b141de5511021942a6866990aea6d111c9042235de90e08f94cf972ca03d"},
- {file = "yarl-1.20.1-cp39-cp39-win32.whl", hash = "sha256:b5f307337819cdfdbb40193cad84978a029f847b0a357fbe49f712063cfc4f06"},
- {file = "yarl-1.20.1-cp39-cp39-win_amd64.whl", hash = "sha256:eae7bfe2069f9c1c5b05fc7fe5d612e5bbc089a39309904ee8b829e322dcad00"},
- {file = "yarl-1.20.1-py3-none-any.whl", hash = "sha256:83b8eb083fe4683c6115795d9fc1cfaf2cbbefb19b3a1cb68f6527460f483a77"},
- {file = "yarl-1.20.1.tar.gz", hash = "sha256:d017a4997ee50c91fd5466cef416231bb82177b93b029906cefc542ce14c35ac"},
-]
-
-[package.dependencies]
-idna = ">=2.0"
-multidict = ">=4.0"
-propcache = ">=0.2.1"
-
-[metadata]
-lock-version = "2.1"
-python-versions = "^3.10"
-content-hash = "516b0f6d98db260a51fe75466b7ddf8e158b02fdff3ca40ba72ce62917056c4f"
diff --git a/pyproject.toml b/pyproject.toml
index 856d79fd..8253adf3 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,68 +1,61 @@
-[tool.poetry]
+[project]
name = "surya-ocr"
-version = "0.17.1"
-description = "OCR, layout, reading order, and table recognition in 90+ languages"
-authors = ["Vik Paruchuri "]
+version = "0.20.0"
+description = "OCR, layout, reading order, and table recognition in 90+ languages."
readme = "README.md"
-license = "GPL-3.0-or-later"
-repository = "https://github.com/VikParuchuri/surya"
+license = { text = "Apache-2.0" }
+authors = [
+ { name = "Vik Paruchuri", email = "vik@datalab.to" },
+]
+requires-python = ">=3.10,<4"
keywords = ["ocr", "pdf", "text detection", "text recognition", "tables"]
-packages = [
- {include = "surya"}
+dependencies = [
+ "transformers>=4.56.1",
+ "torch>=2.7.0,<3",
+ "pydantic>=2.5.3,<3",
+ "pydantic-settings>=2.1.0,<3",
+ "python-dotenv>=1.0.0,<2",
+ "pillow>=10.2.0,<11",
+ "pypdfium2==4.30.0",
+ "filetype>=1.2.0,<2",
+ "click>=8.1.8,<9",
+ "platformdirs>=4.3.6,<5",
+ "opencv-python-headless==4.11.0.86",
+ "openai>=1.55.0,<2",
+ "httpx>=0.27.0,<0.28",
+ "huggingface-hub>=0.26.0,<1",
+ "filelock>=3.16.0,<4",
+ "beautifulsoup4>=4.12.0,<5",
]
-[tool.poetry.dependencies]
-python = "^3.10"
-transformers = ">=4.56.1"
-torch = "^2.7.0"
-pydantic = "^2.5.3"
-pydantic-settings = "^2.1.0"
-python-dotenv = "^1.0.0"
-pillow = "^10.2.0"
-pypdfium2 = "=4.30.0"
-filetype = "^1.2.0"
-click = "^8.1.8"
-platformdirs = "^4.3.6"
-opencv-python-headless = "==4.11.0.86"
-einops = "^0.8.1"
-pre-commit = "^4.2.0"
-
-[tool.poetry.group.dev.dependencies]
-jupyter = "^1.0.0"
-pytesseract = "^0.3.10"
-pymupdf = "^1.23.8"
-datasets = "^2.16.1"
-rapidfuzz = "^3.6.1"
-streamlit = "^1.31.0"
-pytest = "^8.3.4"
-pdftext = "^0.5.1"
-tabulate = "^0.9.0"
+[project.urls]
+Repository = "https://github.com/datalab-to/surya"
-[tool.poetry.scripts]
+[project.scripts]
surya_detect = "surya.scripts.detect_text:detect_text_cli"
surya_ocr = "surya.scripts.ocr_text:ocr_text_cli"
surya_layout = "surya.scripts.detect_layout:detect_layout_cli"
surya_gui = "surya.scripts.run_streamlit_app:streamlit_app_cli"
surya_table = "surya.scripts.table_recognition:table_recognition_cli"
-surya_latex_ocr = "surya.scripts.ocr_latex:ocr_latex_cli"
-texify_gui = "surya.scripts.run_texify_app:texify_app_cli"
+surya_screenshot = "surya.scripts.screenshot_app:main"
+
+[dependency-groups]
+dev = [
+ "pre-commit>=4.2.0",
+ "jupyter>=1.0.0",
+ "pymupdf>=1.23.8",
+ "datasets>=2.16.1",
+ "streamlit>=1.31.0",
+ "pytest>=8.3.4",
+ "pdftext>=0.5.1",
+ "tabulate>=0.9.0",
+ "flask>=3.0.0",
+]
[build-system]
-requires = ["poetry-core"]
-build-backend = "poetry.core.masonry.api"
-
-[[tool.poetry.source]]
-name = "libtpu-releases"
-url = "https://storage.googleapis.com/libtpu-releases/index.html"
-priority = "supplemental"
-
-[[tool.poetry.source]]
-name = "libtpu-wheels"
-url = "https://storage.googleapis.com/libtpu-wheels/index.html"
-priority = "supplemental"
+requires = ["hatchling"]
+build-backend = "hatchling.build"
-[tool.poetry.group.xla]
-optional = true
+[tool.hatch.build.targets.wheel]
+packages = ["surya"]
-[tool.poetry.group.xla.dependencies]
-torch-xla = {version = "^2.4.1", extras = ["tpu"]}
diff --git a/static/docs/multilingual.md b/static/docs/multilingual.md
new file mode 100644
index 00000000..ca8115e7
--- /dev/null
+++ b/static/docs/multilingual.md
@@ -0,0 +1,101 @@
+# Surya 2 — full 91-language results
+
+Overall pass rate: **87.2%** across 91 languages (32,055 tests). Each language
+has between ~30 and ~750 tests.
+
+Sorted alphabetically by language name. See [README](../../README.md#multilingual)
+for the curated subset.
+
+| Code | Language | Score |
+|------|-------------------|------:|
+| `af` | Afrikaans | 87.7% |
+| `sq` | Albanian | 88.9% |
+| `am` | Amharic | 74.6% |
+| `ar` | Arabic | 72.7% |
+| `hy` | Armenian | 90.1% |
+| `as` | Assamese | 86.4% |
+| `az` | Azerbaijani | 91.5% |
+| `eu` | Basque | 85.5% |
+| `be` | Belarusian | 98.5% |
+| `bn` | Bengali | 82.7% |
+| `bs` | Bosnian | 92.5% |
+| `br` | Breton | 93.6% |
+| `bg` | Bulgarian | 95.4% |
+| `my` | Burmese | 88.2% |
+| `ca` | Catalan | 86.4% |
+| `zh` | Chinese | 82.5% |
+| `hr` | Croatian | 92.5% |
+| `cs` | Czech | 85.8% |
+| `da` | Danish | 84.5% |
+| `nl` | Dutch | 86.5% |
+| `en` | English | 92.3% |
+| `eo` | Esperanto | 83.1% |
+| `et` | Estonian | 76.8% |
+| `fi` | Finnish | 87.5% |
+| `fr` | French | 89.3% |
+| `gl` | Galician | 85.5% |
+| `ka` | Georgian | 91.4% |
+| `de` | German | 89.7% |
+| `el` | Greek | 80.7% |
+| `gu` | Gujarati | 83.4% |
+| `ha` | Hausa | 89.6% |
+| `he` | Hebrew | 90.9% |
+| `hi` | Hindi | 82.2% |
+| `hu` | Hungarian | 90.6% |
+| `is` | Icelandic | 89.5% |
+| `id` | Indonesian | 90.3% |
+| `ga` | Irish | 92.8% |
+| `it` | Italian | 93.0% |
+| `ja` | Japanese | 86.2% |
+| `jv` | Javanese | 91.1% |
+| `kn` | Kannada | 79.2% |
+| `kk` | Kazakh | 90.3% |
+| `km` | Khmer | 75.0% |
+| `ko` | Korean | 86.7% |
+| `ku` | Kurdish | 93.9% |
+| `ky` | Kyrgyz | 92.3% |
+| `lo` | Lao | 72.6% |
+| `la` | Latin | 86.1% |
+| `lv` | Latvian | 90.3% |
+| `lt` | Lithuanian | 85.4% |
+| `mk` | Macedonian | 95.3% |
+| `mg` | Malagasy | 95.0% |
+| `ms` | Malay | 91.2% |
+| `ml` | Malayalam | 84.7% |
+| `mr` | Marathi | 85.9% |
+| `mn` | Mongolian | 94.3% |
+| `ne` | Nepali | 84.9% |
+| `no` | Norwegian | 93.6% |
+| `or` | Oriya | 60.0% |
+| `ps` | Pashto | 72.0% |
+| `fa` | Persian | 82.3% |
+| `pl` | Polish | 91.4% |
+| `pt` | Portuguese | 86.1% |
+| `pa` | Punjabi | 76.5% |
+| `ro` | Romanian | 86.9% |
+| `ru` | Russian | 88.8% |
+| `sa` | Sanskrit | 78.8% |
+| `gd` | Scottish Gaelic | 92.5% |
+| `sr` | Serbian | 94.1% |
+| `sd` | Sindhi | 87.3% |
+| `si` | Sinhala | 85.4% |
+| `sk` | Slovak | 90.4% |
+| `sl` | Slovenian | 91.4% |
+| `so` | Somali | 97.9% |
+| `es` | Spanish | 90.7% |
+| `su` | Sundanese | 92.6% |
+| `sw` | Swahili | 93.5% |
+| `sv` | Swedish | 91.4% |
+| `ta` | Tamil | 89.9% |
+| `te` | Telugu | 79.2% |
+| `th` | Thai | 76.4% |
+| `tr` | Turkish | 85.4% |
+| `uk` | Ukrainian | 92.1% |
+| `ur` | Urdu | 68.7% |
+| `ug` | Uyghur | 70.2% |
+| `uz` | Uzbek | 88.9% |
+| `vi` | Vietnamese | 73.2% |
+| `cy` | Welsh | 95.1% |
+| `fy` | Western Frisian | 90.9% |
+| `xh` | Xhosa | 90.3% |
+| `yi` | Yiddish | 82.5% |
diff --git a/static/images/arabic.jpg b/static/images/arabic.jpg
deleted file mode 100644
index 49eb5340..00000000
Binary files a/static/images/arabic.jpg and /dev/null differ
diff --git a/static/images/arabic_layout.jpg b/static/images/arabic_layout.jpg
deleted file mode 100644
index ca2db6d4..00000000
Binary files a/static/images/arabic_layout.jpg and /dev/null differ
diff --git a/static/images/arabic_reading.jpg b/static/images/arabic_reading.jpg
deleted file mode 100644
index df00ae68..00000000
Binary files a/static/images/arabic_reading.jpg and /dev/null differ
diff --git a/static/images/arabic_text.jpg b/static/images/arabic_text.jpg
deleted file mode 100644
index 178ac1c7..00000000
Binary files a/static/images/arabic_text.jpg and /dev/null differ
diff --git a/static/images/benchmark_chart.png b/static/images/benchmark_chart.png
deleted file mode 100644
index d7e6e60c..00000000
Binary files a/static/images/benchmark_chart.png and /dev/null differ
diff --git a/static/images/benchmark_chart_small.png b/static/images/benchmark_chart_small.png
deleted file mode 100644
index 761ac373..00000000
Binary files a/static/images/benchmark_chart_small.png and /dev/null differ
diff --git a/static/images/benchmark_layout_chart.png b/static/images/benchmark_layout_chart.png
deleted file mode 100644
index b9a06c04..00000000
Binary files a/static/images/benchmark_layout_chart.png and /dev/null differ
diff --git a/static/images/benchmark_rec_chart.png b/static/images/benchmark_rec_chart.png
deleted file mode 100644
index a3b03ce2..00000000
Binary files a/static/images/benchmark_rec_chart.png and /dev/null differ
diff --git a/static/images/benchmark_tablerec_acc.png b/static/images/benchmark_tablerec_acc.png
deleted file mode 100644
index 103b49e5..00000000
Binary files a/static/images/benchmark_tablerec_acc.png and /dev/null differ
diff --git a/static/images/benchmark_tablerec_speed.png b/static/images/benchmark_tablerec_speed.png
deleted file mode 100644
index 9c058bd1..00000000
Binary files a/static/images/benchmark_tablerec_speed.png and /dev/null differ
diff --git a/static/images/chi_hind.jpg b/static/images/chi_hind.jpg
deleted file mode 100644
index 2ecbe009..00000000
Binary files a/static/images/chi_hind.jpg and /dev/null differ
diff --git a/static/images/chi_hind_layout.jpg b/static/images/chi_hind_layout.jpg
deleted file mode 100644
index f772cbd7..00000000
Binary files a/static/images/chi_hind_layout.jpg and /dev/null differ
diff --git a/static/images/chi_hind_orig.jpg b/static/images/chi_hind_orig.jpg
deleted file mode 100644
index 088dd397..00000000
Binary files a/static/images/chi_hind_orig.jpg and /dev/null differ
diff --git a/static/images/chi_hind_reading.jpg b/static/images/chi_hind_reading.jpg
deleted file mode 100644
index 338712b6..00000000
Binary files a/static/images/chi_hind_reading.jpg and /dev/null differ
diff --git a/static/images/chi_hind_text.jpg b/static/images/chi_hind_text.jpg
deleted file mode 100644
index b974475e..00000000
Binary files a/static/images/chi_hind_text.jpg and /dev/null differ
diff --git a/static/images/chinese.jpg b/static/images/chinese.jpg
deleted file mode 100644
index 77b91cb6..00000000
Binary files a/static/images/chinese.jpg and /dev/null differ
diff --git a/static/images/chinese_layout.jpg b/static/images/chinese_layout.jpg
deleted file mode 100644
index 27711900..00000000
Binary files a/static/images/chinese_layout.jpg and /dev/null differ
diff --git a/static/images/chinese_reading.jpg b/static/images/chinese_reading.jpg
deleted file mode 100644
index fcf49abf..00000000
Binary files a/static/images/chinese_reading.jpg and /dev/null differ
diff --git a/static/images/chinese_text.jpg b/static/images/chinese_text.jpg
deleted file mode 100644
index 5488d9a1..00000000
Binary files a/static/images/chinese_text.jpg and /dev/null differ
diff --git a/static/images/corporate.png b/static/images/corporate.png
new file mode 100644
index 00000000..2e3f97e4
Binary files /dev/null and b/static/images/corporate.png differ
diff --git a/static/images/corporate_layout.png b/static/images/corporate_layout.png
new file mode 100644
index 00000000..ae90ff0d
Binary files /dev/null and b/static/images/corporate_layout.png differ
diff --git a/static/images/corporate_reading.png b/static/images/corporate_reading.png
new file mode 100644
index 00000000..e1e9d84f
Binary files /dev/null and b/static/images/corporate_reading.png differ
diff --git a/static/images/corporate_tablerec.png b/static/images/corporate_tablerec.png
new file mode 100644
index 00000000..0242ab04
Binary files /dev/null and b/static/images/corporate_tablerec.png differ
diff --git a/static/images/corporate_text.png b/static/images/corporate_text.png
new file mode 100644
index 00000000..e6c4f585
Binary files /dev/null and b/static/images/corporate_text.png differ
diff --git a/static/images/excerpt.png b/static/images/excerpt.png
index bdcd67d6..659e3308 100644
Binary files a/static/images/excerpt.png and b/static/images/excerpt.png differ
diff --git a/static/images/excerpt_layout.png b/static/images/excerpt_layout.png
index aa81d54d..92568e00 100644
Binary files a/static/images/excerpt_layout.png and b/static/images/excerpt_layout.png differ
diff --git a/static/images/excerpt_reading.jpg b/static/images/excerpt_reading.jpg
deleted file mode 100644
index 1ae15c08..00000000
Binary files a/static/images/excerpt_reading.jpg and /dev/null differ
diff --git a/static/images/excerpt_reading.png b/static/images/excerpt_reading.png
new file mode 100644
index 00000000..5afa0e50
Binary files /dev/null and b/static/images/excerpt_reading.png differ
diff --git a/static/images/excerpt_text.png b/static/images/excerpt_text.png
index ec885624..c9f266fe 100644
Binary files a/static/images/excerpt_text.png and b/static/images/excerpt_text.png differ
diff --git a/static/images/form.png b/static/images/form.png
new file mode 100644
index 00000000..3cf2eebd
Binary files /dev/null and b/static/images/form.png differ
diff --git a/static/images/form_layout.png b/static/images/form_layout.png
new file mode 100644
index 00000000..c94ed96f
Binary files /dev/null and b/static/images/form_layout.png differ
diff --git a/static/images/form_reading.png b/static/images/form_reading.png
new file mode 100644
index 00000000..8daec853
Binary files /dev/null and b/static/images/form_reading.png differ
diff --git a/static/images/form_tablerec.png b/static/images/form_tablerec.png
new file mode 100644
index 00000000..bbb4d51e
Binary files /dev/null and b/static/images/form_tablerec.png differ
diff --git a/static/images/form_text.png b/static/images/form_text.png
new file mode 100644
index 00000000..30231f74
Binary files /dev/null and b/static/images/form_text.png differ
diff --git a/static/images/funsd.png b/static/images/funsd.png
deleted file mode 100644
index b23e0c2e..00000000
Binary files a/static/images/funsd.png and /dev/null differ
diff --git a/static/images/funsd_layout.jpg b/static/images/funsd_layout.jpg
deleted file mode 100644
index 7f82a435..00000000
Binary files a/static/images/funsd_layout.jpg and /dev/null differ
diff --git a/static/images/funsd_reading.jpg b/static/images/funsd_reading.jpg
deleted file mode 100644
index 47ec1e13..00000000
Binary files a/static/images/funsd_reading.jpg and /dev/null differ
diff --git a/static/images/funsd_text.jpg b/static/images/funsd_text.jpg
deleted file mode 100644
index 8899635f..00000000
Binary files a/static/images/funsd_text.jpg and /dev/null differ
diff --git a/static/images/gcloud_full_langs.png b/static/images/gcloud_full_langs.png
deleted file mode 100644
index 95fd8749..00000000
Binary files a/static/images/gcloud_full_langs.png and /dev/null differ
diff --git a/static/images/gcloud_rec_bench.png b/static/images/gcloud_rec_bench.png
deleted file mode 100644
index f20d3217..00000000
Binary files a/static/images/gcloud_rec_bench.png and /dev/null differ
diff --git a/static/images/handwritten.png b/static/images/handwritten.png
new file mode 100644
index 00000000..e11872a0
Binary files /dev/null and b/static/images/handwritten.png differ
diff --git a/static/images/handwritten_layout.png b/static/images/handwritten_layout.png
new file mode 100644
index 00000000..be713433
Binary files /dev/null and b/static/images/handwritten_layout.png differ
diff --git a/static/images/handwritten_reading.png b/static/images/handwritten_reading.png
new file mode 100644
index 00000000..0d702182
Binary files /dev/null and b/static/images/handwritten_reading.png differ
diff --git a/static/images/handwritten_tablerec.png b/static/images/handwritten_tablerec.png
new file mode 100644
index 00000000..4be9d3e3
Binary files /dev/null and b/static/images/handwritten_tablerec.png differ
diff --git a/static/images/handwritten_text.png b/static/images/handwritten_text.png
new file mode 100644
index 00000000..a566c6d5
Binary files /dev/null and b/static/images/handwritten_text.png differ
diff --git a/static/images/hindi.jpg b/static/images/hindi.jpg
deleted file mode 100644
index ccbca51c..00000000
Binary files a/static/images/hindi.jpg and /dev/null differ
diff --git a/static/images/hindi_layout.jpg b/static/images/hindi_layout.jpg
deleted file mode 100644
index 26a599f8..00000000
Binary files a/static/images/hindi_layout.jpg and /dev/null differ
diff --git a/static/images/hindi_reading.jpg b/static/images/hindi_reading.jpg
deleted file mode 100644
index 75d6986e..00000000
Binary files a/static/images/hindi_reading.jpg and /dev/null differ
diff --git a/static/images/hindi_text.jpg b/static/images/hindi_text.jpg
deleted file mode 100644
index a0db569a..00000000
Binary files a/static/images/hindi_text.jpg and /dev/null differ
diff --git a/static/images/japanese.jpg b/static/images/japanese.jpg
deleted file mode 100644
index 7a5479e5..00000000
Binary files a/static/images/japanese.jpg and /dev/null differ
diff --git a/static/images/japanese_layout.jpg b/static/images/japanese_layout.jpg
deleted file mode 100644
index 79206066..00000000
Binary files a/static/images/japanese_layout.jpg and /dev/null differ
diff --git a/static/images/japanese_reading.jpg b/static/images/japanese_reading.jpg
deleted file mode 100644
index 26e72768..00000000
Binary files a/static/images/japanese_reading.jpg and /dev/null differ
diff --git a/static/images/japanese_tablerec.png b/static/images/japanese_tablerec.png
deleted file mode 100644
index 05aa9da7..00000000
Binary files a/static/images/japanese_tablerec.png and /dev/null differ
diff --git a/static/images/japanese_text.jpg b/static/images/japanese_text.jpg
deleted file mode 100644
index ab2af988..00000000
Binary files a/static/images/japanese_text.jpg and /dev/null differ
diff --git a/static/images/latex_ocr.png b/static/images/latex_ocr.png
index 3ebc4610..52fc4a7b 100644
Binary files a/static/images/latex_ocr.png and b/static/images/latex_ocr.png differ
diff --git a/static/images/newspaper.png b/static/images/newspaper.png
new file mode 100644
index 00000000..4434c427
Binary files /dev/null and b/static/images/newspaper.png differ
diff --git a/static/images/newspaper_layout.png b/static/images/newspaper_layout.png
new file mode 100644
index 00000000..e08387e4
Binary files /dev/null and b/static/images/newspaper_layout.png differ
diff --git a/static/images/newspaper_reading.png b/static/images/newspaper_reading.png
new file mode 100644
index 00000000..90850db4
Binary files /dev/null and b/static/images/newspaper_reading.png differ
diff --git a/static/images/newspaper_text.png b/static/images/newspaper_text.png
new file mode 100644
index 00000000..5e2cd475
Binary files /dev/null and b/static/images/newspaper_text.png differ
diff --git a/static/images/nyt.jpg b/static/images/nyt.jpg
deleted file mode 100644
index 2f857a1e..00000000
Binary files a/static/images/nyt.jpg and /dev/null differ
diff --git a/static/images/nyt_layout.jpg b/static/images/nyt_layout.jpg
deleted file mode 100644
index 9ab49896..00000000
Binary files a/static/images/nyt_layout.jpg and /dev/null differ
diff --git a/static/images/nyt_order.jpg b/static/images/nyt_order.jpg
deleted file mode 100644
index f98027ba..00000000
Binary files a/static/images/nyt_order.jpg and /dev/null differ
diff --git a/static/images/nyt_text.jpg b/static/images/nyt_text.jpg
deleted file mode 100644
index 5499ad67..00000000
Binary files a/static/images/nyt_text.jpg and /dev/null differ
diff --git a/static/images/olmocr_size_chart.png b/static/images/olmocr_size_chart.png
new file mode 100644
index 00000000..2b5c0d19
Binary files /dev/null and b/static/images/olmocr_size_chart.png differ
diff --git a/static/images/paper.jpg b/static/images/paper.jpg
deleted file mode 100644
index 70b59917..00000000
Binary files a/static/images/paper.jpg and /dev/null differ
diff --git a/static/images/paper_layout.jpg b/static/images/paper_layout.jpg
deleted file mode 100644
index 64df0693..00000000
Binary files a/static/images/paper_layout.jpg and /dev/null differ
diff --git a/static/images/paper_reading.jpg b/static/images/paper_reading.jpg
deleted file mode 100644
index c1675afe..00000000
Binary files a/static/images/paper_reading.jpg and /dev/null differ
diff --git a/static/images/paper_tablerec.png b/static/images/paper_tablerec.png
deleted file mode 100644
index 5ca76c56..00000000
Binary files a/static/images/paper_tablerec.png and /dev/null differ
diff --git a/static/images/paper_text.jpg b/static/images/paper_text.jpg
deleted file mode 100644
index 475e233b..00000000
Binary files a/static/images/paper_text.jpg and /dev/null differ
diff --git a/static/images/pres.png b/static/images/pres.png
deleted file mode 100644
index b33036bf..00000000
Binary files a/static/images/pres.png and /dev/null differ
diff --git a/static/images/pres_layout.jpg b/static/images/pres_layout.jpg
deleted file mode 100644
index 2f9d7411..00000000
Binary files a/static/images/pres_layout.jpg and /dev/null differ
diff --git a/static/images/pres_reading.jpg b/static/images/pres_reading.jpg
deleted file mode 100644
index 7f61e299..00000000
Binary files a/static/images/pres_reading.jpg and /dev/null differ
diff --git a/static/images/pres_tablerec.png b/static/images/pres_tablerec.png
deleted file mode 100644
index d0a839e9..00000000
Binary files a/static/images/pres_tablerec.png and /dev/null differ
diff --git a/static/images/pres_text.jpg b/static/images/pres_text.jpg
deleted file mode 100644
index cf548553..00000000
Binary files a/static/images/pres_text.jpg and /dev/null differ
diff --git a/static/images/rec_acc_table.png b/static/images/rec_acc_table.png
deleted file mode 100644
index 9e5560fe..00000000
Binary files a/static/images/rec_acc_table.png and /dev/null differ
diff --git a/static/images/scanned.png b/static/images/scanned.png
deleted file mode 100644
index 5cb1c0fb..00000000
Binary files a/static/images/scanned.png and /dev/null differ
diff --git a/static/images/scanned_layout.jpg b/static/images/scanned_layout.jpg
deleted file mode 100644
index edae91b5..00000000
Binary files a/static/images/scanned_layout.jpg and /dev/null differ
diff --git a/static/images/scanned_reading.jpg b/static/images/scanned_reading.jpg
deleted file mode 100644
index bbf2bd5e..00000000
Binary files a/static/images/scanned_reading.jpg and /dev/null differ
diff --git a/static/images/scanned_tablerec.png b/static/images/scanned_tablerec.png
index f5371c23..ef5b2d96 100644
Binary files a/static/images/scanned_tablerec.png and b/static/images/scanned_tablerec.png differ
diff --git a/static/images/scanned_tablerec2.png b/static/images/scanned_tablerec2.png
deleted file mode 100644
index 03c9ace0..00000000
Binary files a/static/images/scanned_tablerec2.png and /dev/null differ
diff --git a/static/images/scanned_text.jpg b/static/images/scanned_text.jpg
deleted file mode 100644
index 499e9019..00000000
Binary files a/static/images/scanned_text.jpg and /dev/null differ
diff --git a/static/images/surya_rec_perf.png b/static/images/surya_rec_perf.png
deleted file mode 100644
index 79cb2f93..00000000
Binary files a/static/images/surya_rec_perf.png and /dev/null differ
diff --git a/static/images/table_rec.png b/static/images/table_rec.png
deleted file mode 100644
index 3def4d52..00000000
Binary files a/static/images/table_rec.png and /dev/null differ
diff --git a/static/images/textbook.jpg b/static/images/textbook.jpg
deleted file mode 100644
index f1542ac4..00000000
Binary files a/static/images/textbook.jpg and /dev/null differ
diff --git a/static/images/textbook.png b/static/images/textbook.png
new file mode 100644
index 00000000..9dd414c6
Binary files /dev/null and b/static/images/textbook.png differ
diff --git a/static/images/textbook_layout.jpg b/static/images/textbook_layout.jpg
deleted file mode 100644
index e89405a1..00000000
Binary files a/static/images/textbook_layout.jpg and /dev/null differ
diff --git a/static/images/textbook_layout.png b/static/images/textbook_layout.png
new file mode 100644
index 00000000..c5a3ff18
Binary files /dev/null and b/static/images/textbook_layout.png differ
diff --git a/static/images/textbook_order.jpg b/static/images/textbook_order.jpg
deleted file mode 100644
index fb06135c..00000000
Binary files a/static/images/textbook_order.jpg and /dev/null differ
diff --git a/static/images/textbook_reading.png b/static/images/textbook_reading.png
new file mode 100644
index 00000000..028a5f88
Binary files /dev/null and b/static/images/textbook_reading.png differ
diff --git a/static/images/textbook_text.jpg b/static/images/textbook_text.jpg
deleted file mode 100644
index 0df29d8a..00000000
Binary files a/static/images/textbook_text.jpg and /dev/null differ
diff --git a/static/images/textbook_text.png b/static/images/textbook_text.png
new file mode 100644
index 00000000..82a6ea77
Binary files /dev/null and b/static/images/textbook_text.png differ
diff --git a/surya/common/adetr/decoder.py b/surya/common/adetr/decoder.py
deleted file mode 100644
index 0c6ed116..00000000
--- a/surya/common/adetr/decoder.py
+++ /dev/null
@@ -1,810 +0,0 @@
-from typing import Dict, Optional, Tuple, Union
-
-import torch
-import torch.utils.checkpoint
-from torch import nn
-from transformers import PretrainedConfig
-
-from transformers.activations import ACT2FN
-from transformers.modeling_attn_mask_utils import AttentionMaskConverter
-from transformers.modeling_outputs import BaseModelOutputWithNoAttention
-from transformers.pytorch_utils import ALL_LAYERNORM_LAYERS
-
-from surya.common.pretrained import SuryaPreTrainedModel
-from surya.common.xla import mark_step
-
-_MAX_SQRT_GRADIENT = 1000.0
-
-
-class WrappedEmbedding(nn.Embedding):
- def forward(self, input_ids, *args, **kwargs):
- return super().forward(input_ids)
-
-
-class SuryaADETRDecoderRMSNorm(nn.Module):
- def __init__(self, dim: int, eps: float = 1e-6):
- super().__init__()
- self.eps = eps
- self.weight = nn.Parameter(torch.zeros(dim))
-
- def _norm(self, x):
- variance = x.pow(2).mean(-1, keepdim=True)
-
- # Add clipping to prevent division by zero
- variance = torch.clamp(variance, min=self.eps)
- return x * torch.rsqrt(variance)
-
- def forward(self, x):
- output = self._norm(x.float())
- # Llama does x.to(float16) * w whilst SuryaADETRDecoder is (x * w).to(float16)
- # See https://github.com/huggingface/transformers/pull/29402
- output = output * (1.0 + self.weight.float())
- # Clamp to float16 range
- f16_info = torch.finfo(x.dtype)
- output = output.clamp(min=f16_info.min, max=f16_info.max)
- output = torch.where(
- torch.isnan(output), torch.tensor(0.0, device=output.device), output
- )
- return output.type_as(x)
-
- def extra_repr(self):
- return f"{tuple(self.weight.shape)}, eps={self.eps}"
-
-
-ALL_LAYERNORM_LAYERS.append(SuryaADETRDecoderRMSNorm)
-
-
-class SuryaADETRDecoderRotaryEmbedding(nn.Module):
- def __init__(self, dim, base=10000, device=None):
- super().__init__()
- self.dim = dim
- self.base = base
- inv_freq = 1.0 / (
- self.base
- ** (torch.arange(0, self.dim, 2, dtype=torch.int64).float() / self.dim)
- )
- self.register_buffer("inv_freq", tensor=inv_freq, persistent=False)
-
- @torch.no_grad()
- # Copied from transformers.models.gemma.modeling_gemma.GemmaRotaryEmbedding.forward with Gemma->SuryaADETRDecoder
- def forward(self, x, position_ids, seq_len=None):
- # x: [bs, num_attention_heads, seq_len, head_size]
- self.inv_freq.to(x.device)
- inv_freq_expanded = (
- self.inv_freq[None, :, None].float().expand(position_ids.shape[0], -1, 1)
- )
- position_ids_expanded = position_ids[:, None, :].float()
-
- freqs = (inv_freq_expanded.float() @ position_ids_expanded.float()).transpose(
- 1, 2
- )
- emb = torch.cat((freqs, freqs), dim=-1)
- cos = emb.cos()
- sin = emb.sin()
- return cos.to(dtype=x.dtype), sin.to(dtype=x.dtype)
-
-
-# Copied from transformers.models.llama.modeling_llama.rotate_half
-def rotate_half(x):
- """Rotates half the hidden dims of the input."""
- x1 = x[..., : x.shape[-1] // 2]
- x2 = x[..., x.shape[-1] // 2 :]
- return torch.cat((-x2, x1), dim=-1)
-
-
-# Copied from transformers.models.llama.modeling_llama.apply_rotary_pos_emb
-def apply_rotary_pos_emb(q, k, cos, sin, unsqueeze_dim=1):
- """Applies Rotary Position Embedding to the query and key tensors.
-
- Args:
- q (`torch.Tensor`): The query tensor.
- k (`torch.Tensor`): The key tensor.
- cos (`torch.Tensor`): The cosine part of the rotary embedding.
- sin (`torch.Tensor`): The sine part of the rotary embedding.
- unsqueeze_dim (`int`, *optional*, defaults to 1):
- The 'unsqueeze_dim' argument specifies the dimension along which to unsqueeze cos[position_ids] and
- sin[position_ids] so that they can be properly broadcasted to the dimensions of q and k. For example, note
- that cos[position_ids] and sin[position_ids] have the shape [batch_size, seq_len, head_dim]. Then, if q and
- k have the shape [batch_size, heads, seq_len, head_dim], then setting unsqueeze_dim=1 makes
- cos[position_ids] and sin[position_ids] broadcastable to the shapes of q and k. Similarly, if q and k have
- the shape [batch_size, seq_len, heads, head_dim], then set unsqueeze_dim=2.
- Returns:
- `tuple(torch.Tensor)` comprising of the query and key tensors rotated using the Rotary Position Embedding.
- """
- cos = cos.unsqueeze(unsqueeze_dim)
- sin = sin.unsqueeze(unsqueeze_dim)
- q_embed = (q * cos) + (rotate_half(q) * sin)
- k_embed = (k * cos) + (rotate_half(k) * sin)
- return q_embed, k_embed
-
-
-# Copied from transformers.models.llama.modeling_llama.repeat_kv
-def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
- """
- This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
- num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
- """
- batch, num_key_value_heads, slen, head_dim = hidden_states.shape
- if n_rep == 1:
- return hidden_states
- hidden_states = hidden_states[:, :, None, :, :].expand(
- batch, num_key_value_heads, n_rep, slen, head_dim
- )
- return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
-
-
-class SuryaADETRDecoderSdpaCrossAttention(nn.Module):
- """Multi-headed attention from 'Attention Is All You Need' paper
- Modified for GQA
- """
-
- def __init__(self, config: PretrainedConfig):
- super().__init__()
- self.config = config
- self.attention_dropout = config.attention_dropout
- self.hidden_size = config.hidden_size
- self.num_attention_heads = config.num_attention_heads
- self.head_dim = config.head_dim
- self.num_key_value_heads = config.num_key_value_heads
- self.num_key_value_groups = self.num_attention_heads // self.num_key_value_heads
-
- self.q_proj = nn.Linear(
- self.hidden_size,
- self.num_attention_heads * self.head_dim,
- bias=config.attention_bias,
- )
- self.k_proj = nn.Linear(
- self.config.encoder_hidden_size,
- self.num_key_value_heads * self.head_dim,
- bias=config.attention_bias,
- )
- self.v_proj = nn.Linear(
- self.config.encoder_hidden_size,
- self.num_key_value_heads * self.head_dim,
- bias=config.attention_bias,
- )
- self.o_proj = nn.Linear(
- self.num_attention_heads * self.head_dim, self.hidden_size, bias=True
- )
- self.rotary_emb = SuryaADETRDecoderRotaryEmbedding(
- self.head_dim,
- base=config.rope_theta,
- )
-
- def forward(
- self,
- hidden_states: torch.Tensor,
- encoder_hidden_states: torch.Tensor,
- attention_mask: Optional[torch.Tensor] = None,
- encoder_attention_mask: Optional[torch.Tensor] = None,
- use_cache: bool = False,
- ) -> Tuple[torch.Tensor, Optional[torch.Tensor], Optional[Tuple[torch.Tensor]]]:
- # Encoder attention mask currently ignored
-
- bsz, q_len, _ = hidden_states.size()
- _, v_len, _ = encoder_hidden_states.size()
-
- query_states = self.q_proj(hidden_states)
- query_states = query_states.view(
- bsz, q_len, self.num_attention_heads, self.head_dim
- ).transpose(1, 2)
-
- if self.key_states is None:
- key_states = self.k_proj(encoder_hidden_states)
- value_states = self.v_proj(encoder_hidden_states)
- key_states = key_states.view(
- bsz, v_len, self.num_key_value_heads, self.head_dim
- ).transpose(1, 2)
- value_states = value_states.view(
- bsz, v_len, self.num_key_value_heads, self.head_dim
- ).transpose(1, 2)
- if use_cache:
- self._update_cache(key_states, value_states)
- else:
- key_states = self.key_states
- value_states = self.value_states
-
- key_states = repeat_kv(key_states, self.num_key_value_groups)
- value_states = repeat_kv(value_states, self.num_key_value_groups)
-
- attn_output = torch.nn.functional.scaled_dot_product_attention(
- query_states,
- key_states,
- value_states,
- attn_mask=None,
- dropout_p=self.attention_dropout if self.training else 0.0,
- scale=self.head_dim**-0.5,
- )
-
- attn_output = attn_output.transpose(1, 2).contiguous()
- attn_output = attn_output.view(bsz, q_len, self.hidden_size)
- attn_output = self.o_proj(attn_output)
- return attn_output
-
- def _clear_cache(self):
- if self.value_states is not None:
- del self.value_states
- if self.key_states is not None:
- del self.key_states
-
- def _setup_cache(self, batch_size, device, dtype=None):
- # Setup initial caches
- self.value_states = None
- self.key_states = None
-
- @torch.no_grad()
- def _update_cache(self, key_states, value_states, **cache_kwargs):
- self.value_states = value_states
- self.key_states = key_states
-
-
-class SuryaADETRDecoderSdpaAttention(nn.Module):
- """Multi-headed attention from 'Attention Is All You Need' paper"""
-
- def __init__(self, config: PretrainedConfig, static_cache=False, max_boxes=None):
- super().__init__()
- self.config = config
- self.attention_dropout = config.attention_dropout
- self.hidden_size = config.hidden_size
- self.num_attention_heads = config.num_attention_heads
- self.head_dim = config.head_dim
- self.num_key_value_heads = config.num_key_value_heads
- self.num_key_value_groups = self.num_attention_heads // self.num_key_value_heads
-
- self.q_proj = nn.Linear(
- self.hidden_size,
- self.num_attention_heads * self.head_dim,
- bias=config.attention_bias,
- )
- self.k_proj = nn.Linear(
- self.hidden_size,
- self.num_key_value_heads * self.head_dim,
- bias=config.attention_bias,
- )
- self.v_proj = nn.Linear(
- self.hidden_size,
- self.num_key_value_heads * self.head_dim,
- bias=config.attention_bias,
- )
- self.o_proj = nn.Linear(
- self.num_attention_heads * self.head_dim, self.hidden_size, bias=True
- )
- self.rotary_emb = SuryaADETRDecoderRotaryEmbedding(
- self.head_dim,
- base=config.rope_theta,
- )
-
- self.static_cache = static_cache
- self.max_boxes = max_boxes
-
- def forward(
- self,
- hidden_states: torch.Tensor,
- position_ids: Optional[torch.LongTensor] = None,
- attention_mask: Optional[torch.Tensor] = None,
- cache_position: Optional[torch.LongTensor] = None,
- use_cache: bool = False,
- window_attn: bool = False,
- ) -> Tuple[torch.Tensor, Optional[torch.Tensor], Optional[Tuple[torch.Tensor]]]:
- bsz, q_len, _ = hidden_states.size()
-
- query_states = self.q_proj(hidden_states)
- key_states = self.k_proj(hidden_states)
- value_states = self.v_proj(hidden_states)
-
- # Final is bsz, num_attention_heads, seq_len, head_dim
- query_states = query_states.view(
- bsz, q_len, self.num_attention_heads, self.head_dim
- ).transpose(1, 2)
- key_states = key_states.view(
- bsz, q_len, self.num_key_value_heads, self.head_dim
- ).transpose(1, 2)
- value_states = value_states.view(
- bsz, q_len, self.num_key_value_heads, self.head_dim
- ).transpose(1, 2)
-
- cos, sin = self.rotary_emb(value_states, position_ids, seq_len=None)
- query_states, key_states = apply_rotary_pos_emb(
- query_states, key_states, cos, sin
- )
-
- if use_cache and hasattr(self, "key_states"):
- cache_kwargs = {
- "cache_position": cache_position,
- "window_attn": window_attn,
- }
- key_states, value_states = self._update_cache(
- key_states, value_states, **cache_kwargs
- )
-
- key_states = repeat_kv(key_states, self.num_key_value_groups)
- value_states = repeat_kv(value_states, self.num_key_value_groups)
-
- causal_mask = attention_mask
- if attention_mask is not None:
- # Mask is batch, head, seq_len, kv_len
- causal_mask = causal_mask[:, :, :, : key_states.shape[-2]]
- if cache_position is not None and self.static_cache:
- current_pos = cache_position[-1]
- causal_mask[:, :, :, current_pos + 1 :] = torch.finfo(
- causal_mask.dtype
- ).min
-
- attn_output = torch.nn.functional.scaled_dot_product_attention(
- query_states,
- key_states,
- value_states,
- attn_mask=causal_mask,
- dropout_p=self.attention_dropout if self.training else 0.0,
- scale=self.head_dim**-0.5,
- )
-
- attn_output = attn_output.transpose(1, 2).contiguous()
- attn_output = attn_output.view(bsz, q_len, self.hidden_size)
- attn_output = self.o_proj(attn_output)
- return attn_output
-
- def _setup_cache(self, batch_size, device, dtype=None):
- if dtype is None and self.config.torch_dtype is not None:
- dtype = self.config.torch_dtype
- dtype = dtype if dtype is not None else torch.float32
-
- # Setup initial caches
- self.value_states = None
- self.key_states = None
-
- if self.static_cache:
- cache_shape = (
- batch_size,
- self.num_key_value_heads,
- self.max_boxes,
- self.head_dim,
- )
- self.value_states = torch.zeros(cache_shape, dtype=dtype, device=device)
- self.key_states = torch.zeros(cache_shape, dtype=dtype, device=device)
-
- def _clear_cache(self):
- if self.value_states is not None:
- del self.value_states
- if self.key_states is not None:
- del self.key_states
-
- def _update_static_cache(self, key_states, value_states, **cache_kwargs):
- cache_position = cache_kwargs.get("cache_position")
- k_out, v_out = (
- self.key_states.to(key_states.device),
- self.value_states.to(value_states.device),
- )
-
- k_out[:, :, cache_position] = key_states.to(k_out.dtype)
- v_out[:, :, cache_position] = value_states.to(v_out.dtype)
-
- self.key_states, self.value_states = k_out, v_out
- return k_out, v_out
-
- def _update_dynamic_cache(self, key_states, value_states, **cache_kwargs):
- k_out = key_states
- if self.key_states is not None:
- k_out = torch.cat([self.key_states, key_states], dim=2)
-
- v_out = value_states
- if self.value_states is not None:
- v_out = torch.cat([self.value_states, value_states], dim=2)
-
- self.key_states, self.value_states = k_out, v_out
- return k_out, v_out
-
- @torch.no_grad()
- def _update_cache(self, key_states, value_states, **cache_kwargs):
- if self.static_cache:
- return self._update_static_cache(key_states, value_states, **cache_kwargs)
-
- return self._update_dynamic_cache(key_states, value_states, **cache_kwargs)
-
-
-class SuryaADETRDecoderMlp(nn.Module):
- def __init__(self, config):
- super().__init__()
- self.config = config
- self.hidden_size = config.hidden_size
- self.intermediate_size = config.intermediate_size
- self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
- self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
- self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False)
- if config.hidden_activation is None:
- config.hidden_activation = "gelu_pytorch_tanh"
- hidden_activation = config.hidden_activation
- self.act_fn = ACT2FN[hidden_activation]
-
- def forward(self, x):
- return self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
-
-
-class SuryaADETRDecoderLayer(nn.Module):
- def __init__(self, config, layer_idx, static_cache=False, max_boxes=None):
- super().__init__()
- self.cross_pre_norm = SuryaADETRDecoderRMSNorm(
- config.hidden_size, eps=config.rms_norm_eps
- )
- self.temporal_pre_norm = SuryaADETRDecoderRMSNorm(
- config.hidden_size, eps=config.rms_norm_eps
- )
-
- self.temporal_block = None
- if layer_idx in config.self_attn_layers:
- self.temporal_block = SuryaADETRDecoderSdpaAttention(
- config, static_cache=static_cache, max_boxes=max_boxes
- )
-
- self.cross_attn_block = None
- if layer_idx in config.cross_attn_layers:
- self.cross_attn_block = SuryaADETRDecoderSdpaCrossAttention(config)
-
- self.window_attn = layer_idx not in config.global_attn_layers
- self.channel_pre_norm = SuryaADETRDecoderRMSNorm(
- config.hidden_size, eps=config.rms_norm_eps
- )
- self.mlp_block = SuryaADETRDecoderMlp(config)
-
- self.double_residual_flow = getattr(config, "double_residual_flow", False)
-
- def forward(
- self,
- activations: torch.Tensor,
- position_ids: torch.Tensor,
- attention_mask: torch.Tensor,
- encoder_hidden_states: torch.Tensor = None,
- encoder_attention_mask: torch.Tensor = None,
- cache_position: torch.Tensor = None,
- use_cache: bool = None,
- ) -> Tuple[torch.Tensor, Dict[str, torch.Tensor]]:
- if self.double_residual_flow:
- return self.double_res_forward(
- activations,
- position_ids,
- attention_mask,
- encoder_hidden_states,
- encoder_attention_mask,
- cache_position,
- use_cache,
- )
-
- hidden_states = activations
- if self.cross_attn_block is not None:
- # Do cross-attention on encoder outputs
- cross_attn_inputs = self.cross_pre_norm(hidden_states)
- cross_attn_path = self.cross_attn_block(
- cross_attn_inputs,
- encoder_hidden_states,
- attention_mask,
- encoder_attention_mask,
- use_cache=use_cache,
- )
- hidden_states = cross_attn_path + hidden_states
-
- if self.temporal_block is not None:
- temporal_inputs = self.temporal_pre_norm(
- hidden_states
- ) # RMSNorm introduces slight slight differences
- temporal_path = self.temporal_block(
- temporal_inputs,
- position_ids,
- attention_mask,
- cache_position=cache_position,
- use_cache=use_cache,
- window_attn=self.window_attn,
- )
-
- hidden_states = temporal_path + hidden_states
-
- block_input = hidden_states
- hidden_states = self.channel_pre_norm(block_input)
- hidden_states = self.mlp_block(hidden_states)
- hidden_states = hidden_states + block_input
-
- return hidden_states
-
- def double_res_forward(
- self,
- activations: torch.Tensor,
- position_ids: torch.Tensor,
- attention_mask: torch.Tensor,
- encoder_hidden_states: torch.Tensor = None,
- encoder_attention_mask: torch.Tensor = None,
- cache_position: torch.Tensor = None,
- use_cache: bool = None,
- ) -> Tuple[torch.Tensor, Dict[str, torch.Tensor]]:
- raw_activations = activations
-
- if self.cross_attn_block is not None:
- # Do cross-attention on encoder outputs
- cross_attn_inputs = self.cross_pre_norm(activations)
- cross_attn_path = self.cross_attn_block(
- cross_attn_inputs,
- encoder_hidden_states,
- attention_mask,
- encoder_attention_mask,
- use_cache=use_cache,
- )
- cross_attn_output = cross_attn_path + raw_activations
- else:
- cross_attn_output = raw_activations
-
- if self.temporal_block is not None:
- inputs_normalized = self.temporal_pre_norm(
- cross_attn_output
- ) # RMSNorm introduces slight slight differences
- hidden_states = self.temporal_block(
- inputs_normalized,
- position_ids,
- attention_mask,
- cache_position=cache_position,
- use_cache=use_cache,
- window_attn=self.window_attn,
- )
-
- residual = hidden_states + raw_activations
- else:
- residual = cross_attn_output
-
- hidden_states = self.channel_pre_norm(residual)
- hidden_states = self.mlp_block(hidden_states)
-
- hidden_states = hidden_states + residual
- return hidden_states
-
-
-class SuryaADETRDecoderPreTrainedModel(SuryaPreTrainedModel):
- config_class = PretrainedConfig
- base_model_prefix = "model"
- supports_gradient_checkpointing = True
- _no_split_modules = ["SuryaADETRDecoderLayer"]
- _skip_keys_device_placement = ["cache"]
- _supports_flash_attn_2 = False
- _supports_sdpa = False # we can't compare with eager for now
- _supports_cache_class = True
- _supports_quantized_cache = True
-
- def _init_weights(self, module):
- if isinstance(module, SuryaADETRDecoderSdpaAttention):
- torch.nn.init.normal_(
- module.q_proj.weight, mean=0.0, std=self.config.init_std
- )
- torch.nn.init.normal_(
- module.k_proj.weight, mean=0.0, std=self.config.init_std
- )
- torch.nn.init.normal_(
- module.v_proj.weight, mean=0.0, std=self.config.init_std
- )
-
- torch.nn.init.normal_(
- module.o_proj.weight, mean=0.0, std=self.config.init_std
- )
- elif isinstance(module, nn.Linear):
- torch.nn.init.normal_(module.weight, mean=0.0, std=self.config.init_std)
- if getattr(module, "bias", None) is not None:
- torch.nn.init.zeros_(module.bias)
- elif isinstance(module, nn.Embedding):
- module.weight.data.normal_(mean=0.0, std=self.config.init_std)
- if module.padding_idx is not None:
- module.weight.data[module.padding_idx].zero_()
-
- def _setup_cache(self, config, batch, device, dtype):
- layers = getattr(self, "model", self).layers
- for layer in layers:
- if layer.temporal_block:
- layer.temporal_block._setup_cache(batch, device, dtype)
- if layer.cross_attn_block:
- layer.cross_attn_block._setup_cache(batch, device, dtype)
-
- def _clear_cache(self):
- layers = getattr(self, "model", self).layers
- for layer in layers:
- if layer.temporal_block:
- layer.temporal_block._clear_cache()
- if layer.cross_attn_block:
- layer.cross_attn_block._clear_cache()
-
- def reset_cache(self, batch, device, dtype):
- pass
-
- def _tie_weights(self):
- pass
-
- def tie_weights(self):
- pass
-
-
-class SuryaADETRDecoderModel(SuryaADETRDecoderPreTrainedModel):
- """
- Transformer decoder consisting of *config.num_hidden_layers* layers. Each layer is a [`SuryaADETRDecoderDecoderLayer`]
-
- Args:
- config: PretrainedConfig
- """
-
- def __init__(
- self,
- config: PretrainedConfig,
- embedder: nn.Module = None,
- max_boxes: int = None,
- static_cache: bool = False,
- ):
- super().__init__(config)
- self.padding_idx = config.pad_token_id
- self.vocab_size = config.vocab_size
- self.causal = config.causal
-
- self.embed_tokens = embedder
- self.max_boxes = max_boxes
- self.static_cache = static_cache
-
- self.layers = nn.ModuleList(
- [
- SuryaADETRDecoderLayer(
- config, layer_idx, static_cache=static_cache, max_boxes=max_boxes
- )
- for layer_idx in range(config.num_hidden_layers)
- ]
- )
- self.final_norm = SuryaADETRDecoderRMSNorm(
- config.hidden_size, eps=config.rms_norm_eps
- )
- self.gradient_checkpointing = False
-
- self.register_buffer(
- "normalizer",
- torch.tensor(self.config.hidden_size**0.5, dtype=torch.float32),
- persistent=False,
- )
- # Initialize weights and apply final processing
- self.post_init()
-
- # Copied from transformers.models.llama.modeling_llama.LlamaModel.get_input_embeddings
- def get_input_embeddings(self):
- return self.embed_tokens
-
- # Copied from transformers.models.llama.modeling_llama.LlamaModel.set_input_embeddings
- def set_input_embeddings(self, value):
- self.embed_tokens = value
-
- def forward(
- self,
- input_ids: torch.LongTensor = None,
- input_boxes_counts: torch.LongTensor = None,
- inputs_embeds: Optional[torch.FloatTensor] = None,
- position_ids: Optional[torch.LongTensor] = None,
- attention_mask: Optional[torch.Tensor] = None,
- encoder_hidden_states: Optional[torch.FloatTensor] = None,
- encoder_attention_mask: Optional[torch.FloatTensor] = None,
- cache_position: Optional[torch.LongTensor] = None,
- use_cache: Optional[bool] = None,
- output_hidden_states: Optional[bool] = None,
- return_dict: Optional[bool] = None,
- prefill: bool = False,
- ) -> Union[Tuple, BaseModelOutputWithNoAttention]:
- use_cache = use_cache if use_cache is not None else self.config.use_cache
- return_dict = (
- return_dict if return_dict is not None else self.config.use_return_dict
- )
-
- if self.gradient_checkpointing and self.training and use_cache:
- use_cache = False
-
- inputs_embeds = self.embed_tokens(input_ids, input_boxes_counts)
- hidden_states = inputs_embeds
-
- if use_cache and prefill:
- self._setup_cache(
- self.config,
- hidden_states.shape[0],
- hidden_states.device,
- hidden_states.dtype,
- )
-
- if cache_position is None:
- cache_position = torch.arange(
- hidden_states.shape[1], device=hidden_states.device
- )
- if position_ids is None:
- position_ids = cache_position.unsqueeze(0)
-
- causal_mask = self._update_causal_mask(
- attention_mask, inputs_embeds, cache_position
- )
-
- all_hidden_states = () if output_hidden_states else None
- for i, residual_block in enumerate(self.layers):
- if output_hidden_states:
- all_hidden_states += (hidden_states,)
- if self.gradient_checkpointing and self.training:
- hidden_states = self._gradient_checkpointing_func(
- residual_block.__call__,
- hidden_states,
- position_ids,
- causal_mask,
- encoder_hidden_states,
- encoder_attention_mask,
- cache_position,
- use_cache,
- )
- else:
- hidden_states = residual_block(
- hidden_states,
- position_ids,
- causal_mask,
- encoder_hidden_states,
- encoder_attention_mask,
- cache_position,
- use_cache,
- )
-
- hidden_states = self.final_norm(hidden_states)
-
- # add hidden states from the last decoder layer
- if output_hidden_states:
- all_hidden_states += (hidden_states,)
-
- if not return_dict:
- return tuple(v for v in [hidden_states, all_hidden_states] if v is not None)
-
- return BaseModelOutputWithNoAttention(
- last_hidden_state=hidden_states,
- hidden_states=all_hidden_states,
- )
-
- # TODO: As of torch==2.2.0, the `attention_mask` passed to the model in `generate` is 2D and of dynamic length even when the static
- # KV cache is used. This is an issue for torch.compile which then recaptures cudagraphs at each decode steps due to the dynamic shapes.
- # (`recording cudagraph tree for symint key 13`, etc.), which is VERY slow. A workaround is `@torch.compiler.disable`, but this prevents using
- # `fullgraph=True`. See more context in https://github.com/huggingface/transformers/pull/29114
- # Ignore copy
- def _update_causal_mask(self, attention_mask, input_tensor, cache_position):
- if not self.causal:
- return None
-
- dtype, device = input_tensor.dtype, input_tensor.device
- min_dtype = torch.finfo(dtype).min
- sequence_length = input_tensor.shape[1]
- target_length = max(self.max_boxes, sequence_length)
-
- diagonal = torch.full(
- (sequence_length, target_length),
- fill_value=min_dtype,
- dtype=dtype,
- device=device,
- )
- causal_mask = diagonal
- if sequence_length != 1:
- # Select the upper triangular part of the matrix, but unmask current token (the diagonal)
- # triu will be the min_dtype, everything else is 0 (attended to)
- causal_mask = torch.triu(diagonal, diagonal=1)
-
- causal_mask *= torch.arange(
- target_length, device=device
- ) > cache_position.reshape(-1, 1)
- causal_mask = causal_mask[None, None, :, :].expand(
- input_tensor.shape[0], 1, -1, -1
- )
- if attention_mask is not None:
- causal_mask = (
- causal_mask.clone()
- ) # copy to contiguous memory for in-place edit
- if attention_mask.dim() == 2:
- # Mask positions in the causal mask that are masked in the attention mask
- mask_length = attention_mask.shape[-1]
- padding_mask = causal_mask[..., :mask_length].eq(0.0) * attention_mask[
- :, None, None, :
- ].eq(0.0)
- causal_mask[..., :mask_length] = causal_mask[
- ..., :mask_length
- ].masked_fill(padding_mask, min_dtype)
-
- if attention_mask is not None and attention_mask.device.type == "cuda":
- # Attend to all tokens in fully masked rows in the causal_mask, for example the relevant first rows when
- # using left padding. This is required by F.scaled_dot_product_attention memory-efficient attention path.
- # Details: https://github.com/pytorch/pytorch/issues/110213
- causal_mask = AttentionMaskConverter._unmask_unattended(
- causal_mask, min_dtype
- )
-
- return causal_mask
diff --git a/surya/common/blank.py b/surya/common/blank.py
new file mode 100644
index 00000000..d11c721d
--- /dev/null
+++ b/surya/common/blank.py
@@ -0,0 +1,64 @@
+"""Pixel-content heuristics for detecting blank or near-uniform image regions.
+
+Used by both the layout predictor (drop hallucinated layout blocks over empty
+space) and the recognition predictor (drop hallucinated text blocks from
+full-page OCR, decide whether an empty full-page output is a correct blank-page
+read or a failure).
+
+Two signals, combined:
+ * near-white fraction — most pixels have every RGB channel above a threshold
+ * pixel-value standard deviation — the region is essentially one color
+ (catches uniform-color fills that the white check misses)
+"""
+
+from __future__ import annotations
+
+import numpy as np
+from PIL import Image
+
+
+# Per-channel value at/above which a pixel is considered "near-white".
+# Tolerates the small noise typical of PDF renders at 96 DPI.
+BLANK_WHITE_THRESHOLD = 245
+# Fraction of pixels that must be near-white for a region to count as blank.
+BLANK_PIXEL_FRACTION = 0.99
+# Pixel-value std below which a region is "essentially one color" regardless
+# of what that color is (catches solid-fill rectangles, dark banners, etc.).
+UNIFORM_COLOR_STD = 8.0
+
+
+def near_white_fraction(
+ image: Image.Image, white_threshold: int = BLANK_WHITE_THRESHOLD
+) -> float:
+ """Fraction of pixels where every RGB channel ≥ ``white_threshold``."""
+ arr = np.asarray(image.convert("RGB"))
+ if arr.size == 0:
+ return 0.0
+ return float(np.all(arr >= white_threshold, axis=-1).mean())
+
+
+def is_blank_region(
+ image: Image.Image,
+ *,
+ white_threshold: int = BLANK_WHITE_THRESHOLD,
+ blank_pixel_fraction: float = BLANK_PIXEL_FRACTION,
+ uniform_color_std: float = UNIFORM_COLOR_STD,
+) -> bool:
+ """True iff the image is essentially blank — either mostly near-white or
+ near-uniform color. Use this on a per-block crop or a whole page.
+
+ Returns False for empty (0-pixel) crops so callers don't accidentally
+ treat a degenerate bbox as blank.
+ """
+ arr = np.asarray(image.convert("RGB"))
+ if arr.size == 0:
+ return False
+ if np.all(arr >= white_threshold, axis=-1).mean() > blank_pixel_fraction:
+ return True
+ # Per-channel std — a uniform solid color (e.g., red banner with RGB=(200,50,50))
+ # has each channel constant across pixels, but mixing channels inflates the
+ # aggregate std. Check each channel independently.
+ per_channel_std = arr.reshape(-1, arr.shape[-1]).std(axis=0)
+ if float(per_channel_std.max()) < uniform_color_std:
+ return True
+ return False
diff --git a/surya/common/donut/encoder.py b/surya/common/donut/encoder.py
deleted file mode 100644
index 524e4b70..00000000
--- a/surya/common/donut/encoder.py
+++ /dev/null
@@ -1,955 +0,0 @@
-import collections.abc
-import math
-from dataclasses import dataclass
-from typing import Optional, Tuple, Union
-
-import torch
-import torch.utils.checkpoint
-from torch import nn
-
-from transformers.activations import ACT2FN
-from transformers.pytorch_utils import (
- find_pruneable_heads_and_indices,
- meshgrid,
- prune_linear_layer,
-)
-from transformers.utils import ModelOutput
-from transformers import DonutSwinConfig
-
-from surya.common.pretrained import SuryaPreTrainedModel
-from surya.common.xla import mark_step
-
-_EXPECTED_OUTPUT_SHAPE = [1, 49, 1024]
-
-
-@dataclass
-# Copied from transformers.models.swin.modeling_swin.SwinEncoderOutput with Swin->DonutSwin
-class DonutSwinEncoderOutput(ModelOutput):
- last_hidden_state: torch.FloatTensor = None
- hidden_states: Optional[Tuple[torch.FloatTensor, ...]] = None
- attentions: Optional[Tuple[torch.FloatTensor, ...]] = None
- reshaped_hidden_states: Optional[Tuple[torch.FloatTensor, ...]] = None
-
-
-@dataclass
-class DonutSwinModelOutput(ModelOutput):
- last_hidden_state: torch.FloatTensor = None
-
-
-# Copied from transformers.models.swin.modeling_swin.window_partition
-def window_partition(input_feature, window_size):
- """
- Partitions the given input into windows.
- """
- batch_size, height, width, num_channels = input_feature.shape
- input_feature = input_feature.view(
- batch_size,
- height // window_size,
- window_size,
- width // window_size,
- window_size,
- num_channels,
- )
- windows = (
- input_feature.permute(0, 1, 3, 2, 4, 5)
- .contiguous()
- .view(-1, window_size, window_size, num_channels)
- )
- return windows
-
-
-# Copied from transformers.models.swin.modeling_swin.window_reverse
-def window_reverse(windows, window_size, height, width):
- """
- Merges windows to produce higher resolution features.
- """
- num_channels = windows.shape[-1]
- windows = windows.view(
- -1,
- height // window_size,
- width // window_size,
- window_size,
- window_size,
- num_channels,
- )
- windows = (
- windows.permute(0, 1, 3, 2, 4, 5)
- .contiguous()
- .view(-1, height, width, num_channels)
- )
- return windows
-
-
-# Copied from transformers.models.swin.modeling_swin.SwinEmbeddings with Swin->DonutSwin
-class DonutSwinEmbeddings(nn.Module):
- """
- Construct the patch and position embeddings. Optionally, also the mask token.
- """
-
- def __init__(self, config, use_mask_token=False):
- super().__init__()
-
- self.patch_embeddings = DonutSwinPatchEmbeddings(config)
- num_patches = self.patch_embeddings.num_patches
- self.patch_grid = self.patch_embeddings.grid_size
- self.mask_token = (
- nn.Parameter(torch.zeros(1, 1, config.embed_dim))
- if use_mask_token
- else None
- )
-
- self.position_embeddings = None
- self.row_embeddings = None
- self.column_embeddings = None
- if config.use_absolute_embeddings:
- self.position_embeddings = nn.Parameter(
- torch.zeros(1, num_patches + 1, config.embed_dim)
- )
-
- if hasattr(config, "use_2d_embeddings") and config.use_2d_embeddings:
- self.row_embeddings = nn.Parameter(
- torch.zeros(1, self.patch_grid[0] + 1, config.embed_dim)
- )
- self.column_embeddings = nn.Parameter(
- torch.zeros(1, self.patch_grid[1] + 1, config.embed_dim)
- )
-
- self.norm = nn.LayerNorm(config.embed_dim)
-
- def interpolate_pos_encoding(
- self, embeddings: torch.Tensor, height: int, width: int
- ) -> torch.Tensor:
- """
- This method allows to interpolate the pre-trained position encodings, to be able to use the model on higher
- resolution images.
-
- Source:
- https://github.com/facebookresearch/dino/blob/de9ee3df6cf39fac952ab558447af1fa1365362a/vision_transformer.py#L174
- """
-
- num_patches = embeddings.shape[1] - 1
- num_positions = self.position_embeddings.shape[1] - 1
- if num_patches == num_positions and height == width:
- return self.position_embeddings
- class_pos_embed = self.position_embeddings[:, 0]
- patch_pos_embed = self.position_embeddings[:, 1:]
- dim = embeddings.shape[-1]
- h0 = height // self.config.patch_size
- w0 = width // self.config.patch_size
- # we add a small number to avoid floating point error in the interpolation
- # see discussion at https://github.com/facebookresearch/dino/issues/8
- h0, w0 = h0 + 0.1, w0 + 0.1
- patch_pos_embed = patch_pos_embed.reshape(
- 1, int(math.sqrt(num_positions)), int(math.sqrt(num_positions)), dim
- )
- patch_pos_embed = patch_pos_embed.permute(0, 3, 1, 2)
- patch_pos_embed = nn.functional.interpolate(
- patch_pos_embed,
- scale_factor=(h0 / math.sqrt(num_positions), w0 / math.sqrt(num_positions)),
- mode="bicubic",
- align_corners=False,
- )
- patch_pos_embed = patch_pos_embed.permute(0, 2, 3, 1).view(1, -1, dim)
- return torch.cat((class_pos_embed.unsqueeze(0), patch_pos_embed), dim=1)
-
- def forward(
- self,
- pixel_values: Optional[torch.FloatTensor],
- bool_masked_pos: Optional[torch.BoolTensor] = None,
- interpolate_pos_encoding: bool = False,
- ) -> Tuple[torch.Tensor]:
- _, num_channels, height, width = pixel_values.shape
- embeddings, output_dimensions = self.patch_embeddings(pixel_values)
- embeddings = self.norm(embeddings)
- batch_size, seq_len, _ = embeddings.size()
-
- if bool_masked_pos is not None:
- mask_tokens = self.mask_token.expand(batch_size, seq_len, -1)
- # replace the masked visual tokens by mask_tokens
- mask = bool_masked_pos.unsqueeze(-1).type_as(mask_tokens)
- embeddings = embeddings * (1.0 - mask) + mask_tokens * mask
-
- if self.position_embeddings is not None:
- if interpolate_pos_encoding:
- embeddings = embeddings + self.interpolate_pos_encoding(
- embeddings, height, width
- )
- else:
- embeddings = embeddings + self.position_embeddings[:, :seq_len]
-
- if self.row_embeddings is not None and self.column_embeddings is not None:
- # Repeat the x position embeddings across the y axis like 0, 1, 2, 3, 0, 1, 2, 3, ...
- row_embeddings = self.row_embeddings[
- :, : output_dimensions[0], :
- ].repeat_interleave(output_dimensions[1], dim=1)
- column_embeddings = self.column_embeddings[
- :, : output_dimensions[1], :
- ].repeat(1, output_dimensions[0], 1)
-
- embeddings = embeddings + row_embeddings + column_embeddings
-
- return embeddings, output_dimensions
-
-
-# Copied from transformers.models.swin.modeling_swin.SwinPatchEmbeddings with Swin->DonutSwin
-class DonutSwinPatchEmbeddings(nn.Module):
- """
- This class turns `pixel_values` of shape `(batch_size, num_channels, height, width)` into the initial
- `hidden_states` (patch embeddings) of shape `(batch_size, seq_length, hidden_size)` to be consumed by a
- Transformer.
- """
-
- def __init__(self, config):
- super().__init__()
- image_size, patch_size = config.image_size, config.patch_size
- num_channels, hidden_size = config.num_channels, config.embed_dim
- image_size = (
- image_size
- if isinstance(image_size, collections.abc.Iterable)
- else (image_size, image_size)
- )
- patch_size = (
- patch_size
- if isinstance(patch_size, collections.abc.Iterable)
- else (patch_size, patch_size)
- )
- num_patches = (image_size[1] // patch_size[1]) * (
- image_size[0] // patch_size[0]
- )
- self.image_size = image_size
- self.patch_size = patch_size
- self.num_channels = num_channels
- self.num_patches = num_patches
- self.grid_size = (
- image_size[0] // patch_size[0],
- image_size[1] // patch_size[1],
- )
-
- self.projection = nn.Conv2d(
- num_channels, hidden_size, kernel_size=patch_size, stride=patch_size
- )
-
- def maybe_pad(self, pixel_values, height, width):
- if width % self.patch_size[1] != 0:
- pad_values = (0, self.patch_size[1] - width % self.patch_size[1])
- pixel_values = nn.functional.pad(pixel_values, pad_values)
- if height % self.patch_size[0] != 0:
- pad_values = (0, 0, 0, self.patch_size[0] - height % self.patch_size[0])
- pixel_values = nn.functional.pad(pixel_values, pad_values)
- return pixel_values
-
- def forward(
- self, pixel_values: Optional[torch.FloatTensor]
- ) -> Tuple[torch.Tensor, Tuple[int]]:
- _, num_channels, height, width = pixel_values.shape
- # pad the input to be divisible by self.patch_size, if needed
- pixel_values = self.maybe_pad(pixel_values, height, width)
- embeddings = self.projection(pixel_values)
- _, _, height, width = embeddings.shape
- output_dimensions = (height, width)
- embeddings = embeddings.flatten(2).transpose(1, 2)
-
- return embeddings, output_dimensions
-
-
-# Copied from transformers.models.swin.modeling_swin.SwinPatchMerging
-class DonutSwinPatchMerging(nn.Module):
- """
- Patch Merging Layer.
-
- Args:
- input_resolution (`Tuple[int]`):
- Resolution of input feature.
- dim (`int`):
- Number of input channels.
- norm_layer (`nn.Module`, *optional*, defaults to `nn.LayerNorm`):
- Normalization layer class.
- """
-
- def __init__(
- self,
- input_resolution: Tuple[int],
- dim: int,
- norm_layer: nn.Module = nn.LayerNorm,
- ) -> None:
- super().__init__()
- self.input_resolution = input_resolution
- self.dim = dim
- self.reduction = nn.Linear(4 * dim, 2 * dim, bias=False)
- self.norm = norm_layer(4 * dim)
-
- def maybe_pad(self, input_feature, height, width):
- should_pad = (height % 2 == 1) or (width % 2 == 1)
- if should_pad:
- pad_values = (0, 0, 0, width % 2, 0, height % 2)
- input_feature = nn.functional.pad(input_feature, pad_values)
-
- return input_feature
-
- def forward(
- self, input_feature: torch.Tensor, input_dimensions: Tuple[int, int]
- ) -> torch.Tensor:
- height, width = input_dimensions
- # `dim` is height * width
- batch_size, dim, num_channels = input_feature.shape
-
- input_feature = input_feature.view(batch_size, height, width, num_channels)
- # pad input to be disible by width and height, if needed
- input_feature = self.maybe_pad(input_feature, height, width)
- # [batch_size, height/2, width/2, num_channels]
- input_feature_0 = input_feature[:, 0::2, 0::2, :]
- # [batch_size, height/2, width/2, num_channels]
- input_feature_1 = input_feature[:, 1::2, 0::2, :]
- # [batch_size, height/2, width/2, num_channels]
- input_feature_2 = input_feature[:, 0::2, 1::2, :]
- # [batch_size, height/2, width/2, num_channels]
- input_feature_3 = input_feature[:, 1::2, 1::2, :]
- # batch_size height/2 width/2 4*num_channels
- input_feature = torch.cat(
- [input_feature_0, input_feature_1, input_feature_2, input_feature_3], -1
- )
- input_feature = input_feature.view(
- batch_size, -1, 4 * num_channels
- ) # batch_size height/2*width/2 4*C
-
- input_feature = self.norm(input_feature)
- input_feature = self.reduction(input_feature)
-
- return input_feature
-
-
-# Copied from transformers.models.swin.modeling_swin.SwinSelfAttention with Swin->DonutSwin
-class DonutSwinSelfAttention(nn.Module):
- def __init__(self, config, dim, num_heads, num_kv_heads, window_size):
- super().__init__()
- if dim % num_heads != 0:
- raise ValueError(
- f"The hidden size ({dim}) is not a multiple of the number of attention heads ({num_heads})"
- )
-
- self.num_attention_heads = num_heads
- self.num_kv_heads = num_kv_heads
- self.kv_repeats = self.num_attention_heads // self.num_kv_heads
- self.attention_head_size = int(dim / num_heads)
- self.all_head_size = self.num_attention_heads * self.attention_head_size
- self.kv_head_size = self.num_kv_heads * self.attention_head_size
- self.window_size = (
- window_size
- if isinstance(window_size, collections.abc.Iterable)
- else (window_size, window_size)
- )
-
- self.relative_position_bias_table = nn.Parameter(
- torch.zeros(
- (2 * self.window_size[0] - 1) * (2 * self.window_size[1] - 1), num_heads
- )
- )
-
- # get pair-wise relative position index for each token inside the window
- coords_h = torch.arange(self.window_size[0])
- coords_w = torch.arange(self.window_size[1])
- coords = torch.stack(meshgrid([coords_h, coords_w], indexing="ij"))
- coords_flatten = torch.flatten(coords, 1)
- relative_coords = coords_flatten[:, :, None] - coords_flatten[:, None, :]
- relative_coords = relative_coords.permute(1, 2, 0).contiguous()
- relative_coords[:, :, 0] += self.window_size[0] - 1
- relative_coords[:, :, 1] += self.window_size[1] - 1
- relative_coords[:, :, 0] *= 2 * self.window_size[1] - 1
- relative_position_index = relative_coords.sum(-1)
- self.register_buffer("relative_position_index", relative_position_index)
-
- self.query = nn.Linear(
- self.all_head_size, self.all_head_size, bias=config.qkv_bias
- )
- self.key = nn.Linear(
- self.all_head_size, self.kv_head_size, bias=config.qkv_bias
- )
- self.value = nn.Linear(
- self.all_head_size, self.kv_head_size, bias=config.qkv_bias
- )
-
- def transpose_for_scores(self, x):
- new_x_shape = x.size()[:-1] + (
- self.num_attention_heads,
- self.attention_head_size,
- )
- x = x.view(new_x_shape)
- return x.permute(0, 2, 1, 3)
-
- def transpose_kv_for_scores(self, x, repeats):
- new_x_shape = x.size()[:-1] + (self.num_kv_heads, self.attention_head_size)
- x = x.view(new_x_shape)
- x = x.repeat(
- 1, 1, repeats, 1
- ) # repeat the values for each key-value head to match query dim
- return x.permute(0, 2, 1, 3).contiguous()
-
- def forward(
- self,
- hidden_states: torch.Tensor,
- attention_mask: Optional[torch.FloatTensor] = None,
- head_mask: Optional[torch.FloatTensor] = None,
- output_attentions: Optional[bool] = False,
- ) -> Tuple[torch.Tensor]:
- batch_size, dim, num_channels = hidden_states.shape
- mixed_query_layer = self.query(hidden_states)
-
- # Final is (batch_size, num_attention_heads, seq_len, attention_head_size)
- key_layer = self.transpose_kv_for_scores(
- self.key(hidden_states), self.kv_repeats
- )
- value_layer = self.transpose_kv_for_scores(
- self.value(hidden_states), self.kv_repeats
- )
- query_layer = self.transpose_for_scores(mixed_query_layer)
-
- relative_position_bias = self.relative_position_bias_table[
- self.relative_position_index.view(-1)
- ]
- relative_position_bias = relative_position_bias.view(
- self.window_size[0] * self.window_size[1],
- self.window_size[0] * self.window_size[1],
- -1,
- )
- relative_position_bias = (
- relative_position_bias.permute(2, 0, 1).contiguous().unsqueeze(0)
- )
- relative_position_bias = relative_position_bias.repeat(batch_size, 1, 1, 1)
-
- if attention_mask is None:
- attention_mask = relative_position_bias
- else:
- mask_shape = attention_mask.shape[0]
- repeat_count = batch_size // mask_shape
- attention_mask = attention_mask.repeat(repeat_count, 1, 1).unsqueeze(1)
- attention_mask = attention_mask + relative_position_bias
-
- attn_output = torch.nn.functional.scaled_dot_product_attention(
- query_layer,
- key_layer,
- value_layer,
- attn_mask=attention_mask,
- dropout_p=0.0,
- scale=self.attention_head_size**-0.5,
- )
-
- attn_output = attn_output.transpose(1, 2).contiguous()
- attn_output = attn_output.view(batch_size, dim, num_channels)
-
- outputs = (attn_output,)
- return outputs
-
-
-# Copied from transformers.models.swin.modeling_swin.SwinSelfOutput
-class DonutSwinSelfOutput(nn.Module):
- def __init__(self, config, dim):
- super().__init__()
- self.dense = nn.Linear(dim, dim)
-
- def forward(
- self, hidden_states: torch.Tensor, input_tensor: torch.Tensor
- ) -> torch.Tensor:
- return self.dense(hidden_states)
-
-
-# Copied from transformers.models.swin.modeling_swin.SwinAttention with Swin->DonutSwin
-class DonutSwinAttention(nn.Module):
- def __init__(self, config, dim, num_heads, num_kv_heads, window_size):
- super().__init__()
- self.self = DonutSwinSelfAttention(
- config, dim, num_heads, num_kv_heads, window_size
- )
- self.output = DonutSwinSelfOutput(config, dim)
- self.pruned_heads = set()
-
- def prune_heads(self, heads):
- if len(heads) == 0:
- return
- heads, index = find_pruneable_heads_and_indices(
- heads,
- self.self.num_attention_heads,
- self.self.attention_head_size,
- self.pruned_heads,
- )
-
- # Prune linear layers
- self.self.query = prune_linear_layer(self.self.query, index)
- self.self.key = prune_linear_layer(self.self.key, index)
- self.self.value = prune_linear_layer(self.self.value, index)
- self.output.dense = prune_linear_layer(self.output.dense, index, dim=1)
-
- # Update hyper params and store pruned heads
- self.self.num_attention_heads = self.self.num_attention_heads - len(heads)
- self.self.all_head_size = (
- self.self.attention_head_size * self.self.num_attention_heads
- )
- self.pruned_heads = self.pruned_heads.union(heads)
-
- def forward(
- self,
- hidden_states: torch.Tensor,
- attention_mask: Optional[torch.FloatTensor] = None,
- head_mask: Optional[torch.FloatTensor] = None,
- output_attentions: Optional[bool] = False,
- ) -> Tuple[torch.Tensor]:
- self_outputs = self.self(
- hidden_states, attention_mask, head_mask, output_attentions
- )
- attention_output = self.output(self_outputs[0], hidden_states)
- outputs = (attention_output,) + self_outputs[
- 1:
- ] # add attentions if we output them
- return outputs
-
-
-# Copied from transformers.models.swin.modeling_swin.SwinIntermediate
-class DonutSwinIntermediate(nn.Module):
- def __init__(self, config, dim):
- super().__init__()
- self.dense = nn.Linear(dim, int(config.mlp_ratio * dim))
- if isinstance(config.hidden_act, str):
- self.intermediate_act_fn = ACT2FN[config.hidden_act]
- else:
- self.intermediate_act_fn = config.hidden_act
-
- def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
- hidden_states = self.dense(hidden_states)
- hidden_states = self.intermediate_act_fn(hidden_states)
- return hidden_states
-
-
-# Copied from transformers.models.swin.modeling_swin.SwinOutput
-class DonutSwinOutput(nn.Module):
- def __init__(self, config, dim):
- super().__init__()
- self.dense = nn.Linear(int(config.mlp_ratio * dim), dim)
-
- def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
- return self.dense(hidden_states)
-
-
-# Copied from transformers.models.swin.modeling_swin.SwinLayer with Swin->DonutSwin
-class DonutSwinLayer(nn.Module):
- def __init__(
- self, config, dim, input_resolution, num_heads, num_kv_heads, shift_size=0
- ):
- super().__init__()
- self.chunk_size_feed_forward = config.chunk_size_feed_forward
- self.shift_size = shift_size
- self.window_size = config.window_size
- self.input_resolution = input_resolution
- self.layernorm_before = nn.LayerNorm(dim, eps=config.layer_norm_eps)
- self.attention = DonutSwinAttention(
- config, dim, num_heads, num_kv_heads, window_size=self.window_size
- )
- self.layernorm_after = nn.LayerNorm(dim, eps=config.layer_norm_eps)
- self.intermediate = DonutSwinIntermediate(config, dim)
- self.output = DonutSwinOutput(config, dim)
-
- def set_shift_and_window_size(self, input_resolution):
- if min(input_resolution) <= self.window_size:
- # if window size is larger than input resolution, we don't partition windows
- self.shift_size = int(0)
- self.window_size = (
- torch.min(torch.tensor(input_resolution))
- if torch.jit.is_tracing()
- else min(input_resolution)
- )
-
- def get_attn_mask(self, height, width, dtype, device):
- if self.shift_size > 0:
- # calculate attention mask for SW-MSA
- img_mask = torch.zeros((1, height, width, 1), dtype=dtype, device=device)
- height_slices = (
- slice(0, -self.window_size),
- slice(-self.window_size, -self.shift_size),
- slice(-self.shift_size, None),
- )
- width_slices = (
- slice(0, -self.window_size),
- slice(-self.window_size, -self.shift_size),
- slice(-self.shift_size, None),
- )
- count = 0
- for height_slice in height_slices:
- for width_slice in width_slices:
- img_mask[:, height_slice, width_slice, :] = count
- count += 1
-
- mask_windows = window_partition(img_mask, self.window_size)
- mask_windows = mask_windows.view(-1, self.window_size * self.window_size)
- attn_mask = mask_windows.unsqueeze(1) - mask_windows.unsqueeze(2)
- attn_mask = attn_mask.masked_fill(
- attn_mask != 0, float(-100.0)
- ).masked_fill(attn_mask == 0, float(0.0))
- else:
- attn_mask = None
- return attn_mask
-
- def maybe_pad(self, hidden_states, height, width):
- pad_right = (self.window_size - width % self.window_size) % self.window_size
- pad_bottom = (self.window_size - height % self.window_size) % self.window_size
- pad_values = (0, 0, 0, pad_right, 0, pad_bottom)
- hidden_states = nn.functional.pad(hidden_states, pad_values)
- return hidden_states, pad_values
-
- def forward(
- self,
- hidden_states: torch.Tensor,
- input_dimensions: Tuple[int, int],
- head_mask: Optional[torch.FloatTensor] = None,
- output_attentions: Optional[bool] = False,
- always_partition: Optional[bool] = False,
- ) -> Tuple[torch.Tensor, torch.Tensor]:
- if not always_partition:
- self.set_shift_and_window_size(input_dimensions)
- else:
- pass
- height, width = input_dimensions
- batch_size, _, channels = hidden_states.size()
- shortcut = hidden_states
-
- hidden_states = self.layernorm_before(hidden_states)
-
- hidden_states = hidden_states.view(batch_size, height, width, channels)
-
- # pad hidden_states to multiples of window size
- hidden_states, pad_values = self.maybe_pad(hidden_states, height, width)
-
- _, height_pad, width_pad, _ = hidden_states.shape
- # cyclic shift
- if self.shift_size > 0:
- shifted_hidden_states = torch.roll(
- hidden_states, shifts=(-self.shift_size, -self.shift_size), dims=(1, 2)
- )
- else:
- shifted_hidden_states = hidden_states
-
- # partition windows
- hidden_states_windows = window_partition(
- shifted_hidden_states, self.window_size
- )
- hidden_states_windows = hidden_states_windows.view(
- -1, self.window_size * self.window_size, channels
- )
- attn_mask = self.get_attn_mask(
- height_pad,
- width_pad,
- dtype=hidden_states.dtype,
- device=hidden_states_windows.device,
- )
-
- attention_outputs = self.attention(
- hidden_states_windows,
- attn_mask,
- head_mask,
- output_attentions=output_attentions,
- )
-
- attention_output = attention_outputs[0]
-
- attention_windows = attention_output.view(
- -1, self.window_size, self.window_size, channels
- )
- shifted_windows = window_reverse(
- attention_windows, self.window_size, height_pad, width_pad
- )
-
- # reverse cyclic shift
- if self.shift_size > 0:
- attention_windows = torch.roll(
- shifted_windows, shifts=(self.shift_size, self.shift_size), dims=(1, 2)
- )
- else:
- attention_windows = shifted_windows
-
- was_padded = pad_values[3] > 0 or pad_values[5] > 0
- if was_padded:
- attention_windows = attention_windows[:, :height, :width, :].contiguous()
-
- attention_windows = attention_windows.view(batch_size, height * width, channels)
-
- hidden_states = shortcut + attention_windows
-
- layer_output = self.layernorm_after(hidden_states)
- layer_output = self.intermediate(layer_output)
- layer_output = hidden_states + self.output(layer_output)
-
- layer_outputs = (
- (layer_output, attention_outputs[1])
- if output_attentions
- else (layer_output,)
- )
- return layer_outputs
-
-
-# Copied from transformers.models.swin.modeling_swin.SwinStage with Swin->DonutSwin
-class DonutSwinStage(nn.Module):
- def __init__(
- self,
- config,
- layer_num,
- dim,
- input_resolution,
- depth,
- num_heads,
- num_kv_heads,
- downsample,
- ):
- super().__init__()
- self.config = config
- self.dim = dim
- self.blocks = nn.ModuleList(
- [
- DonutSwinLayer(
- config=config,
- dim=dim,
- input_resolution=input_resolution,
- num_heads=num_heads,
- num_kv_heads=num_kv_heads,
- shift_size=0 if (i % 2 == 0) else config.window_size // 2,
- )
- for i in range(depth)
- ]
- )
-
- # patch merging layer
- if downsample is not None:
- self.downsample = downsample(
- input_resolution, dim=dim, norm_layer=nn.LayerNorm
- )
- else:
- self.downsample = None
-
- self.pointing = False
-
- self.positional_encoding = None
- if config.use_positional_embeddings:
- self.positional_encoding = self.build_2d_sincos_position_embedding(
- input_resolution[1],
- input_resolution[0],
- embed_dim=dim,
- )
-
- @staticmethod
- def build_2d_sincos_position_embedding(
- width,
- height,
- embed_dim=256,
- temperature=10000.0,
- device="cpu",
- dtype=torch.float32,
- ):
- grid_w = torch.arange(int(width), dtype=dtype, device=device)
- grid_h = torch.arange(int(height), dtype=dtype, device=device)
- grid_w, grid_h = torch.meshgrid(grid_w, grid_h, indexing="ij")
- if embed_dim % 4 != 0:
- raise ValueError(
- "Embed dimension must be divisible by 4 for 2D sin-cos position embedding"
- )
- pos_dim = embed_dim // 4
- omega = torch.arange(pos_dim, dtype=dtype, device=device) / pos_dim
- omega = 1.0 / (temperature**omega)
-
- out_w = grid_w.flatten()[..., None] @ omega[None]
- out_h = grid_h.flatten()[..., None] @ omega[None]
-
- return torch.concat(
- [out_w.sin(), out_w.cos(), out_h.sin(), out_h.cos()], dim=1
- )[None, :, :]
-
- def forward(
- self,
- hidden_states: torch.Tensor,
- input_dimensions: Tuple[int, int],
- head_mask: Optional[torch.FloatTensor] = None,
- output_attentions: Optional[bool] = False,
- always_partition: Optional[bool] = False,
- ) -> Tuple[torch.Tensor]:
- height, width = input_dimensions
-
- if self.positional_encoding is not None:
- hidden_states = hidden_states + self.positional_encoding.to(
- hidden_states.dtype
- ).to(hidden_states.device)
-
- for i, layer_module in enumerate(self.blocks):
- layer_head_mask = head_mask[i] if head_mask is not None else None
-
- layer_outputs = layer_module(
- hidden_states,
- input_dimensions,
- layer_head_mask,
- output_attentions,
- always_partition,
- )
-
- hidden_states = layer_outputs[0]
-
- hidden_states_before_downsampling = hidden_states
- if self.downsample is not None:
- height_downsampled, width_downsampled = (height + 1) // 2, (width + 1) // 2
- output_dimensions = (height, width, height_downsampled, width_downsampled)
- hidden_states = self.downsample(
- hidden_states_before_downsampling, input_dimensions
- )
- else:
- output_dimensions = (height, width, height, width)
-
- stage_outputs = (
- hidden_states,
- hidden_states_before_downsampling,
- output_dimensions,
- )
-
- if output_attentions:
- stage_outputs += layer_outputs[1:]
- return stage_outputs
-
-
-# Copied from transformers.models.swin.modeling_swin.SwinEncoder with Swin->DonutSwin
-class DonutSwinEncoder(nn.Module):
- def __init__(self, config, grid_size):
- super().__init__()
- self.num_layers = len(config.depths)
- self.config = config
- self.layers = nn.ModuleList(
- [
- DonutSwinStage(
- config=config,
- layer_num=i_layer,
- dim=int(config.embed_dim * 2**i_layer),
- input_resolution=(
- grid_size[0] // (2**i_layer),
- grid_size[1] // (2**i_layer),
- ),
- depth=config.depths[i_layer],
- num_heads=config.num_heads[i_layer],
- num_kv_heads=config.num_kv_heads[i_layer]
- if hasattr(config, "num_kv_heads")
- else config.num_heads[i_layer],
- downsample=DonutSwinPatchMerging
- if (i_layer < self.num_layers - 1)
- else None,
- )
- for i_layer in range(self.num_layers)
- ]
- )
-
- self.gradient_checkpointing = False
-
- def forward(
- self,
- hidden_states: torch.Tensor,
- input_dimensions: Tuple[int, int],
- head_mask: Optional[torch.FloatTensor] = None,
- output_attentions: Optional[bool] = False,
- output_hidden_states: Optional[bool] = False,
- output_hidden_states_before_downsampling: Optional[bool] = False,
- always_partition: Optional[bool] = False,
- return_dict: Optional[bool] = True,
- ) -> Union[Tuple, DonutSwinEncoderOutput]:
- all_hidden_states = () if output_hidden_states else None
- all_reshaped_hidden_states = () if output_hidden_states else None
- all_self_attentions = () if output_attentions else None
-
- if output_hidden_states:
- batch_size, _, hidden_size = hidden_states.shape
- # rearrange b (h w) c -> b c h w
- reshaped_hidden_state = hidden_states.view(
- batch_size, *input_dimensions, hidden_size
- )
- reshaped_hidden_state = reshaped_hidden_state.permute(0, 3, 1, 2)
- all_hidden_states += (hidden_states,)
- all_reshaped_hidden_states += (reshaped_hidden_state,)
-
- for i, layer_module in enumerate(self.layers):
- layer_head_mask = head_mask[i] if head_mask is not None else None
-
- if self.gradient_checkpointing and self.training:
- layer_outputs = self._gradient_checkpointing_func(
- layer_module.__call__,
- hidden_states,
- input_dimensions,
- layer_head_mask,
- output_attentions,
- always_partition,
- )
- else:
- layer_outputs = layer_module(
- hidden_states,
- input_dimensions,
- layer_head_mask,
- output_attentions,
- always_partition,
- )
-
- hidden_states = layer_outputs[0]
- hidden_states_before_downsampling = layer_outputs[1]
- output_dimensions = layer_outputs[2]
- input_dimensions = (output_dimensions[-2], output_dimensions[-1])
-
- if output_hidden_states and output_hidden_states_before_downsampling:
- batch_size, _, hidden_size = hidden_states_before_downsampling.shape
- # rearrange b (h w) c -> b c h w
- # here we use the original (not downsampled) height and width
- reshaped_hidden_state = hidden_states_before_downsampling.view(
- batch_size,
- *(output_dimensions[0], output_dimensions[1]),
- hidden_size,
- )
- reshaped_hidden_state = reshaped_hidden_state.permute(0, 3, 1, 2)
- all_hidden_states += (hidden_states_before_downsampling,)
- all_reshaped_hidden_states += (reshaped_hidden_state,)
- elif output_hidden_states and not output_hidden_states_before_downsampling:
- batch_size, _, hidden_size = hidden_states.shape
- # rearrange b (h w) c -> b c h w
- reshaped_hidden_state = hidden_states.view(
- batch_size, *input_dimensions, hidden_size
- )
- reshaped_hidden_state = reshaped_hidden_state.permute(0, 3, 1, 2)
- all_hidden_states += (hidden_states,)
- all_reshaped_hidden_states += (reshaped_hidden_state,)
-
- if output_attentions:
- all_self_attentions += layer_outputs[3:]
-
- if not return_dict:
- return tuple(
- v
- for v in [hidden_states, all_hidden_states, all_self_attentions]
- if v is not None
- )
-
- return DonutSwinEncoderOutput(
- last_hidden_state=hidden_states,
- hidden_states=all_hidden_states,
- attentions=all_self_attentions,
- reshaped_hidden_states=all_reshaped_hidden_states,
- )
-
-
-# Copied from transformers.models.swin.modeling_swin.SwinPreTrainedModel with Swin->DonutSwin
-class DonutSwinPreTrainedModel(SuryaPreTrainedModel):
- """
- An abstract class to handle weights initialization and a simple interface for downloading and loading pretrained
- models.
- """
-
- config_class = DonutSwinConfig
- base_model_prefix = "swin"
- main_input_name = "pixel_values"
- supports_gradient_checkpointing = True
- _no_split_modules = ["DonutSwinStage"]
-
- def _init_weights(self, module):
- """Initialize the weights"""
- if isinstance(module, (nn.Linear, nn.Conv2d)):
- # Slightly different from the TF version which uses truncated_normal for initialization
- # cf https://github.com/pytorch/pytorch/pull/5617
- module.weight.data.normal_(mean=0.0, std=self.config.initializer_range)
- if module.bias is not None:
- module.bias.data.zero_()
- elif isinstance(module, nn.LayerNorm):
- module.bias.data.zero_()
- module.weight.data.fill_(1.0)
diff --git a/surya/common/donut/processor.py b/surya/common/donut/processor.py
deleted file mode 100644
index c27a57e3..00000000
--- a/surya/common/donut/processor.py
+++ /dev/null
@@ -1,196 +0,0 @@
-from typing import Dict, Union, Optional, List, Iterable
-
-import cv2
-from torch import TensorType
-from transformers import ImageProcessingMixin
-from transformers.image_processing_utils import BatchFeature
-from transformers.image_transforms import pad, normalize
-from transformers.image_utils import (
- ImageInput,
- ChannelDimension,
- make_list_of_images,
- get_image_size,
-)
-import numpy as np
-from PIL import Image
-import PIL
-from transformers.utils import IMAGENET_STANDARD_MEAN, IMAGENET_STANDARD_STD
-
-from surya.common.s3 import S3DownloaderMixin
-from surya.settings import settings
-
-
-class SuryaEncoderImageProcessor(S3DownloaderMixin, ImageProcessingMixin):
- def __init__(
- self,
- *args,
- max_size=None,
- align_long_axis=False,
- rescale_factor: Union[int, float] = 1 / 255,
- image_mean: Optional[Union[float, List[float]]] = None,
- image_std: Optional[Union[float, List[float]]] = None,
- **kwargs,
- ):
- super().__init__(*args, **kwargs)
-
- self.patch_size = kwargs.get("patch_size", (4, 4))
- self.max_size = max_size
- self.do_align_long_axis = align_long_axis
- self.resample = Image.Resampling.BILINEAR
- self.rescale_factor = rescale_factor
- self.image_mean = (
- image_mean if image_mean is not None else IMAGENET_STANDARD_MEAN
- )
- self.image_std = image_std if image_std is not None else IMAGENET_STANDARD_STD
-
- def __call__(self, images, **kwargs) -> PIL.Image.Image:
- """Preprocess an image or a batch of images."""
- return self.preprocess(images, **kwargs)
-
- @classmethod
- def numpy_resize(cls, image: np.ndarray, size, interpolation=cv2.INTER_LANCZOS4):
- max_width, max_height = size["width"], size["height"]
-
- resized_image = cv2.resize(
- image, (max_width, max_height), interpolation=interpolation
- )
- resized_image = resized_image.transpose(2, 0, 1)
-
- return resized_image
-
- def process_inner(self, images: List[np.ndarray]):
- assert images[0].shape[2] == 3 # RGB input images, channel dim last
-
- if self.do_align_long_axis:
- # Rotate if the bbox is wider than it is tall
- images = [
- SuryaEncoderImageProcessor.align_long_axis(
- image, size=self.max_size, input_data_format=ChannelDimension.LAST
- )
- for image in images
- ]
-
- # Verify that the image is wider than it is tall
- for img in images:
- assert img.shape[1] >= img.shape[0]
-
- # This also applies the right channel dim format, to channel x height x width
- images = [
- SuryaEncoderImageProcessor.numpy_resize(img, self.max_size, self.resample)
- for img in images
- ]
- assert images[0].shape[0] == 3 # RGB input images, channel dim first
-
- # Convert to float32 for rescale/normalize
- images = [img.astype(np.float32) for img in images]
-
- # Pads with 255 (whitespace)
- # Pad to max size to improve performance
- max_size = self.max_size
- images = [
- SuryaEncoderImageProcessor.pad_image(
- image=image,
- size=max_size,
- input_data_format=ChannelDimension.FIRST,
- pad_value=settings.RECOGNITION_PAD_VALUE,
- )
- for image in images
- ]
-
- # Rescale and normalize
- for idx in range(len(images)):
- images[idx] = (images[idx].astype(np.float64) * self.rescale_factor).astype(
- np.float32
- )
-
- images = [
- SuryaEncoderImageProcessor.normalize(
- img,
- mean=self.image_mean,
- std=self.image_std,
- input_data_format=ChannelDimension.FIRST,
- )
- for img in images
- ]
-
- return images
-
- def preprocess(
- self,
- images: ImageInput,
- return_tensors: Optional[Union[str, TensorType]] = None,
- **kwargs,
- ) -> PIL.Image.Image:
- images = make_list_of_images(images)
-
- # Convert to numpy for later processing steps
- images = [np.array(img) for img in images]
- images = self.process_inner(images)
-
- data = {"pixel_values": images}
- return BatchFeature(data=data, tensor_type=return_tensors)
-
- @classmethod
- def pad_image(
- cls,
- image: np.ndarray,
- size: Dict[str, int],
- data_format: Optional[Union[str, ChannelDimension]] = None,
- input_data_format: Optional[Union[str, ChannelDimension]] = None,
- pad_value: float = 0.0,
- ) -> np.ndarray:
- output_height, output_width = size["height"], size["width"]
- input_height, input_width = get_image_size(image, channel_dim=input_data_format)
-
- delta_width = output_width - input_width
- delta_height = output_height - input_height
-
- assert delta_width >= 0 and delta_height >= 0
-
- pad_top = delta_height // 2
- pad_left = delta_width // 2
-
- pad_bottom = delta_height - pad_top
- pad_right = delta_width - pad_left
-
- padding = ((pad_top, pad_bottom), (pad_left, pad_right))
- return pad(
- image,
- padding,
- data_format=data_format,
- input_data_format=input_data_format,
- constant_values=pad_value,
- )
-
- @classmethod
- def align_long_axis(
- cls, image: np.ndarray, size: Dict[str, int], **kwargs
- ) -> np.ndarray:
- input_height, input_width = image.shape[:2]
- output_height, output_width = size["height"], size["width"]
-
- if (output_width < output_height and input_width > input_height) or (
- output_width > output_height and input_width < input_height
- ):
- image = np.rot90(image, 3)
-
- return image
-
- @classmethod
- def normalize(
- cls,
- image: np.ndarray,
- mean: Union[float, Iterable[float]],
- std: Union[float, Iterable[float]],
- data_format: Optional[Union[str, ChannelDimension]] = None,
- input_data_format: Optional[Union[str, ChannelDimension]] = None,
- **kwargs,
- ) -> np.ndarray:
- return normalize(
- image,
- mean=mean,
- std=std,
- data_format=data_format,
- input_data_format=input_data_format,
- **kwargs,
- )
diff --git a/surya/common/polygon.py b/surya/common/polygon.py
index d719ac6c..80e7aec1 100644
--- a/surya/common/polygon.py
+++ b/surya/common/polygon.py
@@ -80,23 +80,6 @@ def fit_to_bounds(self, bounds):
corner[1] = max(min(corner[1], bounds[3]), bounds[1])
self.polygon = new_corners
- def merge(self, other):
- x1 = min(self.bbox[0], other.bbox[0])
- y1 = min(self.bbox[1], other.bbox[1])
- x2 = max(self.bbox[2], other.bbox[2])
- y2 = max(self.bbox[3], other.bbox[3])
- self.polygon = [[x1, y1], [x2, y1], [x2, y2], [x1, y2]]
-
- def merge_left(self, other):
- x1 = min(self.bbox[0], other.bbox[0])
- self.polygon[0][0] = x1
- self.polygon[3][0] = x1
-
- def merge_right(self, other):
- x2 = max(self.bbox[2], other.bbox[2])
- self.polygon[1][0] = x2
- self.polygon[2][0] = x2
-
def expand(self, x_margin: float, y_margin: float):
new_polygon = []
x_margin = x_margin * self.width
@@ -112,33 +95,6 @@ def expand(self, x_margin: float, y_margin: float):
new_polygon.append([int(poly[0] - x_margin), int(poly[1] + y_margin)])
self.polygon = new_polygon
- def intersection_polygon(self, other) -> List[List[float]]:
- new_poly = []
- for i in range(4):
- if i == 0:
- new_corner = [
- max(self.polygon[0][0], other.polygon[0][0]),
- max(self.polygon[0][1], other.polygon[0][1]),
- ]
- elif i == 1:
- new_corner = [
- min(self.polygon[1][0], other.polygon[1][0]),
- max(self.polygon[1][1], other.polygon[1][1]),
- ]
- elif i == 2:
- new_corner = [
- min(self.polygon[2][0], other.polygon[2][0]),
- min(self.polygon[2][1], other.polygon[2][1]),
- ]
- elif i == 3:
- new_corner = [
- max(self.polygon[3][0], other.polygon[3][0]),
- min(self.polygon[3][1], other.polygon[3][1]),
- ]
- new_poly.append(new_corner)
-
- return new_poly
-
def intersection_area(self, other, x_margin=0, y_margin=0):
x_overlap = self.x_overlap(other, x_margin)
y_overlap = self.y_overlap(other, y_margin)
@@ -158,44 +114,9 @@ def y_overlap(self, other, y_margin=0):
- max(self.bbox[1] - y_margin, other.bbox[1] - y_margin),
)
- def intersection_pct(self, other, x_margin=0, y_margin=0):
- assert 0 <= x_margin <= 1
- assert 0 <= y_margin <= 1
- if self.area == 0:
- return 0
-
- if x_margin:
- x_margin = int(min(self.width, other.width) * x_margin)
- if y_margin:
- y_margin = int(min(self.height, other.height) * y_margin)
-
- intersection = self.intersection_area(other, x_margin, y_margin)
- return intersection / self.area
-
- def shift(self, x_shift: float | None = None, y_shift: float | None = None):
- if x_shift is not None:
- for corner in self.polygon:
- corner[0] += x_shift
- if y_shift is not None:
- for corner in self.polygon:
- corner[1] += y_shift
-
- def clamp(self, bbox: List[float]):
- for corner in self.polygon:
- corner[0] = max(min(corner[0], bbox[2]), bbox[0])
- corner[1] = max(min(corner[1], bbox[3]), bbox[1])
-
@property
def center(self):
return [(self.bbox[0] + self.bbox[2]) / 2, (self.bbox[1] + self.bbox[3]) / 2]
- def distance(self, other):
- center = self.center
- other_center = other.center
-
- return (
- (center[0] - other_center[0]) ** 2 + (center[1] - other_center[1]) ** 2
- ) ** 0.5
-
def __hash__(self):
return hash(tuple(self.bbox))
diff --git a/surya/common/predictor.py b/surya/common/predictor.py
index 442bee6a..d7e6c726 100644
--- a/surya/common/predictor.py
+++ b/surya/common/predictor.py
@@ -1,6 +1,6 @@
from typing import Optional
+
import torch
-import torch.nn.functional as F
from surya.common.load import ModelLoader
from surya.settings import settings
@@ -30,26 +30,20 @@ def __init__(
if dtype is None:
dtype = self.torch_dtype
- self.model = None
- self.processor = None
loader = self.model_loader_cls(checkpoint)
-
self.model = loader.model(device, dtype, attention_implementation)
self.processor = loader.processor()
-
self._disable_tqdm = settings.DISABLE_TQDM
def to(self, device_dtype: torch.device | str | None = None):
- model_moved = False
if hasattr(self, "model") and self.model:
self.model.to(device_dtype)
- model_moved = True
- if hasattr(self, "foundation_predictor") and self.foundation_predictor:
- self.foundation_predictor.model.to(device_dtype)
- model_moved = True
-
- if not model_moved:
- raise ValueError("Model not loaded")
+ return
+ # Predictors that don't own a torch model (e.g. VLM-backed predictors that
+ # rely on an external server) treat .to() as a no-op.
+ if hasattr(self, "manager") and self.manager is not None:
+ return
+ raise ValueError("Model not loaded")
def get_batch_size(self):
batch_size = self.batch_size
@@ -59,21 +53,5 @@ def get_batch_size(self):
batch_size = self.default_batch_sizes[settings.TORCH_DEVICE_MODEL]
return batch_size
- @staticmethod
- def pad_to_batch_size(tensor: torch.Tensor, batch_size: int):
- current_batch_size = tensor.shape[0]
- if current_batch_size >= batch_size:
- return tensor
-
- if len(tensor.shape) == 1:
- # If tensor is 1D, we need to pad it to the batch size
- pad_size = batch_size - current_batch_size
- return F.pad(tensor, (0, pad_size), mode="constant", value=0)
-
- pad_size = batch_size - current_batch_size
- padding = (0, 0) * (tensor.dim() - 1) + (0, pad_size)
-
- return F.pad(tensor, padding, mode="constant", value=0)
-
def __call__(self, *args, **kwargs):
raise NotImplementedError()
diff --git a/surya/common/surya/__init__.py b/surya/common/surya/__init__.py
deleted file mode 100644
index a32e3b26..00000000
--- a/surya/common/surya/__init__.py
+++ /dev/null
@@ -1,803 +0,0 @@
-import warnings
-from typing import Optional, Tuple, TypedDict
-from dataclasses import dataclass
-
-import torch
-from torch import nn
-import torch.nn.functional as F
-from transformers.modeling_outputs import CausalLMOutputWithPast
-from transformers.cache_utils import Cache
-from transformers.modeling_attn_mask_utils import AttentionMaskConverter
-
-from surya.common.pretrained import SuryaPreTrainedModel
-from surya.common.s3 import S3DownloaderMixin
-from surya.common.surya.config import SuryaModelConfig
-from surya.common.surya.decoder import SuryaDecoderModel
-from surya.common.surya.embedder import SimpleTokenEmbedder
-from surya.common.surya.encoder import SuryaEncoderModel
-from surya.common.util import pad_to_batch_size, pad_to_batch_size_repeat
-from surya.common.xla import get_nearest_pad
-from surya.settings import settings
-
-from surya.logging import get_logger
-
-logger = get_logger()
-
-
-@dataclass
-class SuryaModelOutput(CausalLMOutputWithPast):
- bbox_logits: torch.FloatTensor = None
- lm_logits: torch.FloatTensor = None
-
-
-class FlashAttentionKwargs(TypedDict, total=False):
- """
- Keyword arguments for Flash Attention with Compile.
-
- Attributes:
- cu_seq_lens_q (`torch.LongTensor`, *optional*)
- Gets cumlative sequence length for query state.
- cu_seq_lens_k (`torch.LongTensor`, *optional*)
- Gets cumlative sequence length for key state.
- max_length_q (`int`, *optional*):
- Maximum sequence length for query state.
- max_length_k (`int`, *optional*):
- Maximum sequence length for key state.
- """
-
- cu_seq_lens_q: Optional[torch.LongTensor]
- cu_seq_lens_k: Optional[torch.LongTensor]
- max_length_q: Optional[int]
- max_length_k: Optional[int]
-
-
-class KwargsForCausalLM(FlashAttentionKwargs): ...
-
-
-class DistanceProjection(nn.Module):
- def __init__(self, in_features: int, out_features: int):
- super().__init__()
- self.fc1 = nn.Linear(in_features, out_features)
- self.act = nn.SiLU()
- self.fc2 = nn.Linear(out_features, out_features)
-
- def forward(self, x: torch.Tensor) -> torch.Tensor:
- x = self.fc1(x)
- x = self.act(x)
- x = self.fc2(x)
- return x
-
- def init_weights(self):
- nn.init.xavier_uniform_(self.fc1.weight)
- nn.init.xavier_uniform_(self.fc2.weight)
- nn.init.zeros_(self.fc1.bias)
- nn.init.zeros_(self.fc2.bias)
-
-
-class BboxHead(nn.Module):
- def __init__(self, in_features: int, out_features: int):
- super().__init__()
- self.proj_layers = nn.ModuleList(
- [nn.Linear(in_features, in_features) for _ in range(6)]
- )
- self.act = nn.SiLU()
- self.out_proj = nn.Linear(in_features, out_features)
-
- def forward(self, x: torch.Tensor) -> torch.Tensor:
- for layer in self.proj_layers:
- x = layer(x)
- x = self.act(x)
-
- x = self.out_proj(x)
- return x
-
-
-class SuryaModel(S3DownloaderMixin, SuryaPreTrainedModel):
- config_class = SuryaModelConfig
- supports_gradient_checkpointing = True
- _skip_keys_device_placement = ["past_key_values"]
- _supports_flash_attn_2 = True
- _supports_sdpa = True
- _supports_flex_attn = True
- _supports_cache_class = True
- _supports_quantized_cache = True
- _supports_static_cache = True
- _supports_attention_backend = True
- main_input_name = "input_ids"
- _tied_weights_keys = ["lm_head.weight"]
-
- def __init__(
- self,
- config: SuryaModelConfig,
- embedder: SimpleTokenEmbedder = None,
- vision_encoder: SuryaEncoderModel = None,
- decoder: SuryaDecoderModel = None,
- **kwargs,
- ):
- super().__init__(config, **kwargs)
-
- if vision_encoder is None:
- vision_encoder = SuryaEncoderModel(config.vision_encoder)
-
- if decoder is None:
- decoder = SuryaDecoderModel(config.decoder)
-
- if embedder is None:
- embedder = SimpleTokenEmbedder(config)
-
- self.vision_encoder = vision_encoder
- self.decoder = decoder
- self.embedder = embedder
-
- # Simple encoding for image patches
- self.img_w_embed = nn.Embedding(
- self.config.image_embed_encoding_size,
- self.config.hidden_size,
- )
-
- self.img_h_embed = nn.Embedding(
- self.config.image_embed_encoding_size,
- self.config.hidden_size,
- )
-
- # Tying configs
- self.vision_encoder.config = self.config.vision_encoder
- self.decoder.config = self.config.decoder
-
- self.bbox_head = BboxHead(config.hidden_size, 6)
- self.lm_head = nn.Linear(config.hidden_size, config.vocab_size)
-
- if (
- self.config.multi_output_distance is not None
- and self.config.multi_output_distance > 0
- ):
- self.multi_output_projections = nn.ModuleList(
- [
- DistanceProjection(
- in_features=config.hidden_size, out_features=config.hidden_size
- )
- for _ in range(self.config.multi_output_distance)
- ]
- )
-
- def tie_weights(self):
- self._tie_weights()
-
- def _tie_weights(self):
- # Tie weights of lm head and token embedder
- self._tie_or_clone_weights(self.lm_head, self.embedder.token_embed)
-
- def get_output_embeddings(self) -> nn.Module:
- return self.lm_head
-
- def get_input_embeddings(self) -> nn.Module:
- return self.embedder.token_embed
-
- def set_output_embeddings(self, new_embeddings: nn.Module):
- self.lm_head = new_embeddings
-
- def set_input_embeddings(self, new_embeddings: nn.Module):
- self.embedder.token_embed = new_embeddings
-
- def maybe_static_pad_image_inputs(
- self,
- chunk_pixels: torch.Tensor,
- chunk_grid_thw: torch.Tensor,
- actual_chunk_len: int,
- encoder_chunk_size: int,
- ) -> Tuple[torch.Tensor, torch.Tensor]:
- valid_embed_len = actual_chunk_len // (
- self.vision_encoder.spatial_merge_size**2
- )
- if settings.FOUNDATION_STATIC_CACHE and actual_chunk_len < encoder_chunk_size:
- padding_len = encoder_chunk_size - actual_chunk_len
- chunk_pixels = F.pad(
- chunk_pixels,
- (0, 0, 0, padding_len),
- mode="constant",
- value=0.0,
- )
-
- padding_grid = torch.tensor(
- [[1, 2, padding_len // 2]],
- device=chunk_grid_thw.device,
- dtype=chunk_grid_thw.dtype,
- )
- chunk_grid_thw = torch.cat([chunk_grid_thw, padding_grid], dim=0)
-
- return chunk_pixels, chunk_grid_thw, valid_embed_len
-
- def get_image_embeddings(
- self,
- pixel_values: torch.Tensor,
- grid_thw: torch.Tensor,
- encoder_chunk_size: int,
- valid_batch_size: torch.Tensor | None = None,
- max_batch_size: int | None = None,
- ):
- # embed all images with the vision encoder after they have already been tiled and flattened into a single batch
- chunks = [0]
- grid_chunks = [0]
- curr_chunk_len = 0
- curr_seq_len = 0
- for i in range(len(grid_thw)):
- curr_chunk_len += (grid_thw[i][0] * grid_thw[i][1] * grid_thw[i][2]).item()
- if curr_chunk_len > encoder_chunk_size:
- chunks.append(curr_chunk_len + curr_seq_len)
- curr_seq_len += curr_chunk_len
- curr_chunk_len = 0
- grid_chunks.append(i + 1)
-
- if curr_chunk_len > 0:
- chunks.append(pixel_values.shape[0])
- grid_chunks.append(len(grid_thw))
-
- assert curr_chunk_len + curr_seq_len == pixel_values.shape[0], (
- f"Mismatch in encoder chunking, {curr_chunk_len} + {curr_seq_len} != {pixel_values.shape[0]}"
- )
-
- logger.debug(
- f"Chunking encoder sequence into {len(chunks) - 1} chunks of size {encoder_chunk_size} with lengths {chunks} and grids {grid_chunks}"
- )
- embeddings = []
- for i in range(len(chunks) - 1):
- start = chunks[i]
- end = chunks[i + 1]
- grid_start = grid_chunks[i]
- grid_end = grid_chunks[i + 1]
-
- chunk_pixels = pixel_values[start:end]
- chunk_grid_thw = grid_thw[grid_start:grid_end]
- actual_chunk_len = end - start
- chunk_pixels, chunk_grid_thw, valid_embed_len = (
- self.maybe_static_pad_image_inputs(
- chunk_pixels, chunk_grid_thw, actual_chunk_len, encoder_chunk_size
- )
- )
-
- chunk_embeddings = self.vision_encoder.embed_images(
- image_batch=chunk_pixels.unsqueeze(0).to(device=self.device),
- grid_thw=chunk_grid_thw.unsqueeze(0).to(device=self.device),
- )
- embeddings.append(chunk_embeddings[:valid_embed_len].squeeze(0))
-
- if len(embeddings) == 0:
- raise ValueError(
- "No image embeddings were generated. Check the input images and grid sizes."
- )
- elif len(embeddings) == 1:
- embeddings = embeddings[0]
- else:
- embeddings = torch.cat(embeddings, dim=0)
-
- encoding_2d = self.get_2d_learned_embeddings(
- grid_thw,
- device=embeddings.device,
- bbox_size=self.config.image_embed_encoding_multiplier,
- )
- assert embeddings.shape[0] == encoding_2d.shape[0], (
- f"Mismatch in image embedding seq len: {embeddings.shape} vs {encoding_2d.shape}"
- )
- assert embeddings.shape[1] == encoding_2d.shape[1], (
- f"Mismatch in image embedding token counts: {embeddings.shape} vs {encoding_2d.shape}"
- )
-
- embeddings = embeddings + encoding_2d
-
- return embeddings
-
- def embed_ids_boxes_images(
- self,
- input_ids,
- image_embeddings,
- encoder_chunk_size: int,
- valid_batch_size: torch.Tensor | None = None,
- input_boxes: torch.Tensor | None = None,
- embed_boxes: torch.Tensor | None = None,
- ):
- """
- Insert embedded image tiles into the corresponding positions into the full input sequence
-
- Positions to insert new tokens are indicated by the special image token index
- """
- # This is batched in the inner call
- inputs_embeds = self.embedder.embed(
- input_tokens=input_ids, input_boxes=input_boxes, embed_boxes=embed_boxes
- )
-
- if image_embeddings is not None:
- special_image_mask = (input_ids == self.config.image_token_id).unsqueeze(-1)
- special_image_mask = special_image_mask.expand_as(inputs_embeds)
- if inputs_embeds[special_image_mask].numel() != image_embeddings.numel():
- n_image_tokens = torch.sum((input_ids == self.config.image_token_id))
- n_image_features = image_embeddings.shape[0] * image_embeddings.shape[1]
- warnings.warn(
- f"Image features and image tokens do not match: tokens {n_image_tokens}, features {n_image_features}. This may lead to unexpected results"
- )
- image_features = image_embeddings.to(inputs_embeds.dtype)
- inputs_embeds = inputs_embeds.masked_scatter(
- special_image_mask, image_features
- )
- else:
- assert (input_ids == self.config.image_token_id).sum() == 0, (
- "Image tokens were present in the input but no input images were provided"
- )
-
- return inputs_embeds
-
- def get_2d_learned_embeddings(
- self,
- grid_thw,
- device: str | torch.device = "cpu",
- bbox_size: int = 256,
- ):
- all_embeddings = []
- for grid_t, grid_h, grid_w in grid_thw:
- llm_grid_h, llm_grid_w = (
- grid_h // self.config.merge_size,
- grid_w // self.config.merge_size,
- )
-
- # Scale to 0-1024
- llm_grid_h = (
- torch.arange(llm_grid_h, device=device)
- / max(1, (llm_grid_h - 1))
- * bbox_size
- )
- llm_grid_w = (
- torch.arange(llm_grid_w, device=device)
- / max(1, (llm_grid_w - 1))
- * bbox_size
- )
-
- llm_grid_w_idx = llm_grid_w.to(torch.long)
- llm_grid_h_idx = llm_grid_h.to(torch.long)
-
- llm_grid_w = self.img_w_embed(llm_grid_w_idx)
- llm_grid_h = self.img_h_embed(llm_grid_h_idx)
-
- full_grid = llm_grid_h[:, None] + llm_grid_w[None, :]
-
- flattened = full_grid.flatten(
- 0, 1
- ) # Flatten first dimension, so they are seq_len x embed_dim
- all_embeddings.append(flattened)
- return torch.concat(
- all_embeddings, dim=0
- ) # Shape is num_image_tokens x embed_dim
-
- def get_logits(self, hidden_states):
- assert hidden_states.shape[1] == 1, (
- "Multi output predictions only applied on the last token"
- )
-
- all_lm_logits = []
- all_bbox_logits = []
-
- current_hidden = hidden_states
-
- # Loop includes initial prediction (i=0) plus multi_out_distance additional predictions
- for i in range(self.config.multi_output_distance + 1):
- if i > 0:
- current_hidden = self.multi_output_projections[i - 1](current_hidden)
-
- lm_logits = self.lm_head(current_hidden)
- bbox_logits = F.sigmoid(self.bbox_head(current_hidden))
-
- all_lm_logits.append(lm_logits)
- all_bbox_logits.append(bbox_logits)
-
- # Concatenate along sequence dimension (dim=1)
- final_lm_logits = torch.cat(all_lm_logits, dim=1)
- final_bbox_logits = torch.cat(all_bbox_logits, dim=1)
-
- return final_lm_logits, final_bbox_logits
-
- def forward(
- self,
- input_ids=None,
- image_embeddings=None,
- labels=None,
- image_tiles=None,
- grid_thw=None,
- inputs_embeds=None,
- attention_mask=None,
- position_ids=None,
- cache_position=None,
- past_key_values=None,
- output_hidden_states=False,
- output_attentions=False,
- use_cache=False,
- encoder_chunk_size=32768,
- cache_idxs=None,
- num_valid_tokens=None,
- prefill=True,
- text_lengths=None,
- valid_batch_size: torch.Tensor = None,
- input_boxes=None,
- embed_boxes=None,
- logits_to_keep=None,
- **kwargs: KwargsForCausalLM,
- ):
- if any([
- input_ids is None,
- position_ids is None,
- cache_position is None,
- (
- prefill
- and not (
- (image_tiles is not None and grid_thw is not None)
- or image_embeddings is not None
- )
- ),
- ]):
- raise ValueError(
- "`input_ids`, `position_ids`, and `cache_position` **must** be specified. "
- "For prefill, you must provide either (`image_tiles` and `grid_thw`) or `image_embeddings`."
- )
-
-
- inputs_embeds = self.embed_ids_boxes_images(
- input_ids, image_embeddings, encoder_chunk_size, valid_batch_size, input_boxes, embed_boxes
- )
-
- # Handling flash attention kwargs outside the decoder to speed up + avoid graph breaks inside the decoder
- # Skipped during decoding since not required
- if self.decoder.config._attn_implementation == "flash_attention_2" and prefill:
- # Needed for CPU -> GPU
- from surya.common.surya.flash_attn_utils import _get_unpad_data
- batch_size, query_length, _ = inputs_embeds.shape
- indices_k, cu_seqlens_k, max_seqlen_in_batch_k = _get_unpad_data(
- attention_mask
- )
- kwargs["batch_size"] = batch_size
- kwargs["query_length"] = query_length
- kwargs["indices_k"] = indices_k
- kwargs["cu_seqlens_k"] = cu_seqlens_k
- kwargs["max_seqlen_in_batch_k"] = max_seqlen_in_batch_k
-
- causal_mask = self._update_causal_mask(
- attention_mask,
- inputs_embeds,
- cache_position,
- past_key_values,
- output_attentions,
- )
-
- attention_mask = causal_mask
- outputs = self.decoder(
- inputs_embeds=inputs_embeds,
- attention_mask=attention_mask,
- position_ids=position_ids,
- cache_position=cache_position,
- past_key_values=past_key_values,
- return_dict=True,
- use_cache=use_cache,
- cache_idxs=cache_idxs,
- num_valid_tokens=num_valid_tokens,
- prefill=prefill,
- text_lengths=text_lengths,
- **kwargs,
- )
-
- hidden_states = outputs.last_hidden_state
- if logits_to_keep is not None:
- hidden_states = hidden_states[:, -logits_to_keep:, :]
- hidden_states = hidden_states.contiguous()
-
- loss = None
- if labels is not None:
- # Training, return full logits
- lm_logits = self.lm_head(hidden_states)
- bbox_logits = None
- vocab_size = lm_logits.shape[-1]
- labels = torch.roll(labels, shifts=-1, dims=-1)
- loss = F.cross_entropy(
- lm_logits.view(-1, vocab_size), labels.view(-1), reduction="mean"
- )
- else:
- lm_logits, bbox_logits = self.get_logits(hidden_states)
-
- return SuryaModelOutput(
- loss=loss,
- bbox_logits=bbox_logits,
- lm_logits=lm_logits,
- hidden_states=outputs.hidden_states if output_hidden_states else None,
- attentions=outputs.attentions if output_attentions else None,
- past_key_values=outputs.past_key_values,
- )
-
- def _update_causal_mask(
- self,
- attention_mask: torch.Tensor,
- input_tensor: torch.Tensor,
- cache_position: torch.Tensor,
- past_key_values: Cache,
- output_attentions: bool,
- ):
- if self.decoder.config._attn_implementation == "flash_attention_2":
- return attention_mask
-
- # We always pass in a 2D attention mask from the processor - In both static and dynamic cache cases
- dtype, device = input_tensor.dtype, input_tensor.device
- min_dtype = torch.finfo(dtype).min
- sequence_length = input_tensor.shape[1]
- target_length = (
- attention_mask.shape[-1]
- if isinstance(attention_mask, torch.Tensor)
- else past_key_values.max_cache_len
- )
-
- # In case the provided `attention` mask is 2D, we generate a causal mask here (4D).
- causal_mask = self._prepare_4d_causal_attention_mask_with_cache_position(
- attention_mask,
- sequence_length=sequence_length,
- target_length=target_length,
- dtype=dtype,
- device=device,
- cache_position=cache_position,
- batch_size=input_tensor.shape[0],
- config=self.config,
- past_key_values=past_key_values,
- )
-
- if (
- self.config._attn_implementation == "sdpa"
- and attention_mask is not None
- and attention_mask.device.type in ["cuda", "xpu"]
- and not output_attentions
- ):
- # Attend to all tokens in fully masked rows in the causal_mask, for example the relevant first rows when
- # using left padding. This is required by F.scaled_dot_product_attention memory-efficient attention path.
- # Details: https://github.com/pytorch/pytorch/issues/110213
- causal_mask = AttentionMaskConverter._unmask_unattended(
- causal_mask, min_dtype
- )
-
- return causal_mask
-
- @staticmethod
- def _prepare_4d_causal_attention_mask_with_cache_position(
- attention_mask: torch.Tensor,
- sequence_length: int,
- target_length: int,
- dtype: torch.dtype,
- device: torch.device,
- cache_position: torch.Tensor,
- batch_size: int,
- config: SuryaModelConfig,
- past_key_values: Cache,
- ):
- """
- Creates a causal 4D mask of shape `(batch_size, 1, query_length, key_value_length)` from a 2D mask of shape
- `(batch_size, key_value_length)`, or if the input `attention_mask` is already 4D, do nothing.
-
- Args:
- attention_mask (`torch.Tensor`):
- A 2D attention mask of shape `(batch_size, key_value_length)` or a 4D attention mask of shape `(batch_size, 1, query_length, key_value_length)`.
- sequence_length (`int`):
- The sequence length being processed.
- target_length (`int`):
- The target length: when generating with static cache, the mask should be as long as the static cache, to account for the 0 padding, the part of the cache that is not filled yet.
- dtype (`torch.dtype`):
- The dtype to use for the 4D attention mask.
- device (`torch.device`):
- The device to plcae the 4D attention mask on.
- cache_position (`torch.Tensor`):
- Indices depicting the position of the input sequence tokens in the sequence. Shape `(batch_size, sequence_length)`.
- batch_size (`torch.Tensor`):
- Batch size.
- config (`Qwen2Config`):
- The model's configuration class
- past_key_values (`Cache`):
- The cache class that is being used currently to generate
- """
- if attention_mask is not None and attention_mask.dim() == 4:
- # In this case we assume that the mask comes already in inverted form and requires no inversion or slicing.
- causal_mask = attention_mask
- else:
- min_dtype = torch.finfo(dtype).min
- causal_mask = torch.full(
- (sequence_length, target_length),
- fill_value=min_dtype,
- dtype=dtype,
- device=device,
- )
- # Batch-aware diagonal attend mask
- diagonal_attend_mask = torch.arange(target_length, device=device).unsqueeze(
- 0
- ) > cache_position.unsqueeze(-1)
- causal_mask = (
- causal_mask.unsqueeze(0) * diagonal_attend_mask
- ) # (batch_size, seq_len, target_len)
- causal_mask = causal_mask[
- :, None, :, :
- ] # (batch_size, 1, seq_len, target_len)
- if attention_mask is not None:
- causal_mask = (
- causal_mask.clone()
- ) # copy to contiguous memory for in-place edit
- if attention_mask.shape[-1] > target_length:
- attention_mask = attention_mask[:, :target_length]
- mask_length = attention_mask.shape[-1]
- padding_mask = causal_mask[:, :, :, :mask_length] + attention_mask[
- :, None, None, :
- ].to(causal_mask.device)
- padding_mask = padding_mask == 0
- causal_mask[:, :, :, :mask_length] = causal_mask[
- :, :, :, :mask_length
- ].masked_fill(padding_mask, min_dtype)
- return causal_mask
-
-class SuryaXLAModel(SuryaModel):
- def get_image_embeddings(
- self,
- pixel_values: torch.Tensor,
- grid_thw: torch.Tensor,
- encoder_chunk_size: int,
- valid_batch_size: torch.Tensor | None = None,
- max_batch_size: int | None = None,
- ):
- # embed all images with the vision encoder after they have already been tiled and flattened into a single batch
- unpadded_max_grid_size = (
- (grid_thw[:, 0] * grid_thw[:, 1] * grid_thw[:, 2]).max().item()
- )
- max_grid_size = get_nearest_pad(
- unpadded_max_grid_size,
- ) # If we need zero padding, we still need to allocate a bit of room for the extra grid_thw
-
- # Always need 2 items in each row batch
- if max_grid_size == unpadded_max_grid_size:
- max_grid_size += 16
-
- full_image_grid = torch.zeros(
- (valid_batch_size, max_grid_size, pixel_values.shape[-1]),
- dtype=pixel_values.dtype,
- )
-
- # Roll out into a full grid
- seq_len = 0
- row_grids = []
- for i in range(valid_batch_size):
- curr_sample_len = grid_thw[i][0] * grid_thw[i][1] * grid_thw[i][2]
- full_image_grid[i, -curr_sample_len:] = pixel_values[
- seq_len : seq_len + curr_sample_len
- ]
- padded_len = max_grid_size - curr_sample_len
- if padded_len > 0:
- row_grid = torch.tensor(
- [
- [1, 4, padded_len // 4],
- grid_thw[i].tolist(),
- ],
- dtype=torch.long,
- )
- else:
- row_grid = torch.tensor(
- [
- grid_thw[i].tolist(),
- ],
- dtype=torch.long,
- )
-
- row_grids.append(row_grid)
- seq_len += curr_sample_len
-
- # bsz, 2, 3
- row_grids = torch.stack(row_grids, dim=0)
-
- if settings.FOUNDATION_STATIC_CACHE:
- # Pad to max batch size, repeat the final row
- row_grids = pad_to_batch_size_repeat(
- row_grids,
- batch_size=max_batch_size,
- )
- full_image_grid = pad_to_batch_size(
- full_image_grid,
- batch_size=max_batch_size,
- )
-
- full_image_grid = full_image_grid.to(self.device)
-
- embeddings = self.vision_encoder.embed_images(
- image_batch=full_image_grid, grid_thw=row_grids.to(self.device)
- )
-
- encoding_2d = self.get_2d_learned_embeddings(
- row_grids,
- bbox_size=self.config.image_embed_encoding_multiplier,
- )
- embeddings += encoding_2d
-
- return embeddings
-
- def embed_ids_boxes_images(
- self,
- input_ids,
- image_embeddings,
- encoder_chunk_size: int,
- valid_batch_size: torch.Tensor | None = None,
- input_boxes: torch.Tensor | None = None,
- embed_boxes: torch.Tensor | None = None,
- ):
- """
- Insert embedded image tiles into the corresponding positions into the full input sequence
-
- Positions to insert new tokens are indicated by the special image token index
- """
- # This is batched in the inner call
- inputs_embeds = self.embedder.embed(
- input_tokens=input_ids, input_boxes=input_boxes, embed_boxes=embed_boxes
- )
-
- if image_embeddings is not None:
- image_token_id_tensor = torch.tensor(
- self.config.image_token_id,
- device=inputs_embeds.device,
- dtype=torch.long,
- )
- mask = input_ids == image_token_id_tensor
- last_image_token_pos = (
- mask.size(1)
- - 1
- - mask.flip(dims=[1]).long().argmax(dim=1, keepdim=True)
- )
- # Calculate start position to replace N positions ending at (and including) the last image token
- start_positions = last_image_token_pos - image_embeddings[0].shape[0]
- batch_size, insert_len = image_embeddings.shape[:2]
-
- # Create position indices for each insertion
- pos_indices = torch.arange(
- insert_len, device=inputs_embeds.device
- ).unsqueeze(0)
- insert_positions = start_positions + pos_indices
-
- idx = insert_positions.unsqueeze(-1).expand(
- -1, -1, inputs_embeds.size(-1)
- ) # [B,N,D]
- inputs_embeds = inputs_embeds.scatter(1, idx, image_embeddings)
-
- inputs_embeds = inputs_embeds * (
- input_ids != self.config.pad_token_id
- ).unsqueeze(-1).to(inputs_embeds.dtype)
- return inputs_embeds
-
- def get_2d_learned_embeddings(
- self,
- grid_thw,
- bbox_size: int = 256,
- ):
- dev = grid_thw.device
- all_row_coords = []
- all_col_coords = []
- for row_grid in grid_thw:
- merge = self.config.merge_size
-
- # per-sample grid sizes after merge
- H = (row_grid[:, 1] // merge).long() # (B,)
- W = (row_grid[:, 2] // merge).long() # (B,)
-
- row_coords = torch.cat(
- [
- torch.linspace(0, bbox_size, steps=int(h), device=dev)
- .round()
- .repeat_interleave(w) # repeat each row value w times
- for h, w in zip(H.tolist(), W.tolist())
- ]
- ) # (full_grid_size,)
-
- col_coords = torch.cat(
- [
- torch.linspace(0, bbox_size, steps=int(w), device=dev)
- .round()
- .repeat(int(h)) # tile the column vector h times
- for h, w in zip(H.tolist(), W.tolist())
- ]
- ) # (full_grid_size,)
- all_row_coords.append(row_coords)
- all_col_coords.append(col_coords)
- row_coords = torch.stack(all_row_coords, dim=0).to(self.device)
- col_coords = torch.stack(all_col_coords, dim=0).to(self.device)
-
- emb = self.img_h_embed(row_coords.long()) + self.img_w_embed(col_coords.long())
- return emb
diff --git a/surya/common/surya/config.py b/surya/common/surya/config.py
deleted file mode 100644
index 5fbd6730..00000000
--- a/surya/common/surya/config.py
+++ /dev/null
@@ -1,87 +0,0 @@
-from typing import Optional
-from transformers import PretrainedConfig
-
-from surya.common.s3 import S3DownloaderMixin
-from surya.common.surya.encoder.config import SuryaEncoderConfig
-from surya.common.surya.decoder.config import SuryaDecoderConfig
-
-
-class SuryaModelConfig(S3DownloaderMixin, PretrainedConfig):
- model_type = "surya-multimodal-foundation"
- is_composition = True
-
- def __init__(
- self,
- vocab_size=65536,
- bbox_size=1025,
- blank_bbox_token_id=1025,
- bos_token_id=0,
- eos_token_id=1,
- pad_token_id=2,
- image_token_id=3,
- register_token_ids=(4, 5, 6, 7),
- eoi_token_id=8,
- beacon_token_id=9,
- special_token_count=4,
- max_sequence_length=1536,
- special_ocr_tokens=None,
- vision_encoder=None,
- decoder=None,
- tasks: dict | None = None,
- bbox_embed_size: int = 64,
- num_register_tokens: int = 4,
- image_embed_encoding_size: int = 1024,
- image_embed_encoding_multiplier: int = 256,
- num_beacon_tokens: int = 1,
- beacon_token_interval: int = 4096,
- sliding_window: Optional[int] = None,
- multi_output_distance: int = 4,
- max_multi_out: int = 8,
- **kwargs,
- ):
- super().__init__(**kwargs)
- self.is_encoder_decoder = False
- self.vocab_size = vocab_size
- self.bbox_size = bbox_size
- self.blank_bbox_token_id = blank_bbox_token_id
- self.image_token_id = image_token_id
- self.bos_token_id = bos_token_id
- self.eos_token_id = eos_token_id
- self.pad_token_id = pad_token_id
- self.eoi_token_id = eoi_token_id
- self.beacon_token_id = beacon_token_id
- self.special_ocr_tokens = special_ocr_tokens
- self.special_token_count = special_token_count # pad, bos, etc, tokens
- self.max_sequence_length = max_sequence_length
- self.tasks = tasks
- self.tie_word_embeddings = True
- self.bbox_embed_size = bbox_embed_size
- self.num_register_tokens = num_register_tokens
- self.register_token_ids = register_token_ids
- self.image_embed_encoding_size = image_embed_encoding_size
- self.image_embed_encoding_multiplier = image_embed_encoding_multiplier
- self.num_beacon_tokens = num_beacon_tokens
- self.beacon_token_interval = beacon_token_interval
- self.sliding_window = sliding_window
- self.multi_output_distance = multi_output_distance
- self.max_multi_out = max_multi_out
-
- if self.sliding_window is None:
- self.sliding_window = self.max_sequence_length
-
- if isinstance(vision_encoder, dict):
- vision_encoder = SuryaEncoderConfig(**vision_encoder)
- elif vision_encoder is None:
- vision_encoder = SuryaEncoderConfig()
- self.vision_encoder = vision_encoder
-
- if isinstance(decoder, dict):
- decoder = SuryaDecoderConfig(**decoder)
- elif decoder is None:
- decoder = SuryaDecoderConfig()
- self.decoder = decoder
-
- self.hidden_size = self.decoder.hidden_size
-
- self.patch_size = self.vision_encoder.spatial_patch_size
- self.merge_size = self.vision_encoder.spatial_merge_size
diff --git a/surya/common/surya/decoder/__init__.py b/surya/common/surya/decoder/__init__.py
deleted file mode 100644
index 7cb2324d..00000000
--- a/surya/common/surya/decoder/__init__.py
+++ /dev/null
@@ -1,528 +0,0 @@
-from typing import Callable, List, Optional, Tuple, Union
-
-import torch
-from torch import nn
-
-from transformers.activations import ACT2FN
-from transformers.cache_utils import (
- Cache,
-)
-from transformers.modeling_flash_attention_utils import FlashAttentionKwargs
-from transformers.modeling_outputs import (
- BaseModelOutputWithPast,
-)
-from transformers.modeling_rope_utils import ROPE_INIT_FUNCTIONS
-from transformers.modeling_utils import ALL_ATTENTION_FUNCTIONS
-from transformers.processing_utils import Unpack
-from transformers.utils import (
- logging,
-)
-
-from surya.common.pretrained import SuryaPreTrainedModel
-from surya.common.surya.decoder.config import SuryaDecoderConfig
-
-
-logger = logging.get_logger(__name__)
-
-
-class Qwen2MLP(nn.Module):
- def __init__(self, config):
- super().__init__()
- self.config = config
- self.hidden_size = config.hidden_size
- self.intermediate_size = config.intermediate_size
- self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
- self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
- self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False)
- self.act_fn = ACT2FN[config.hidden_act]
-
- def forward(self, x):
- down_proj = self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
- return down_proj
-
-
-def rotate_half(x):
- """Rotates half the hidden dims of the input."""
- x1 = x[..., : x.shape[-1] // 2]
- x2 = x[..., x.shape[-1] // 2 :]
- return torch.cat((-x2, x1), dim=-1)
-
-
-def apply_rotary_pos_emb(q, k, cos, sin, position_ids=None, unsqueeze_dim=1):
- """Applies Rotary Position Embedding to the query and key tensors.
-
- Args:
- q (`torch.Tensor`): The query tensor.
- k (`torch.Tensor`): The key tensor.
- cos (`torch.Tensor`): The cosine part of the rotary embedding.
- sin (`torch.Tensor`): The sine part of the rotary embedding.
- position_ids (`torch.Tensor`, *optional*):
- Deprecated and unused.
- unsqueeze_dim (`int`, *optional*, defaults to 1):
- The 'unsqueeze_dim' argument specifies the dimension along which to unsqueeze cos[position_ids] and
- sin[position_ids] so that they can be properly broadcasted to the dimensions of q and k. For example, note
- that cos[position_ids] and sin[position_ids] have the shape [batch_size, seq_len, head_dim]. Then, if q and
- k have the shape [batch_size, heads, seq_len, head_dim], then setting unsqueeze_dim=1 makes
- cos[position_ids] and sin[position_ids] broadcastable to the shapes of q and k. Similarly, if q and k have
- the shape [batch_size, seq_len, heads, head_dim], then set unsqueeze_dim=2.
- Returns:
- `tuple(torch.Tensor)` comprising of the query and key tensors rotated using the Rotary Position Embedding.
- """
- cos = cos.unsqueeze(unsqueeze_dim)
- sin = sin.unsqueeze(unsqueeze_dim)
- q_embed = (q * cos) + (rotate_half(q) * sin)
- k_embed = (k * cos) + (rotate_half(k) * sin)
- return q_embed, k_embed
-
-
-def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
- """
- This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
- num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
- """
- batch, num_key_value_heads, slen, head_dim = hidden_states.shape
- if n_rep == 1:
- return hidden_states
- hidden_states = hidden_states[:, :, None, :, :].expand(
- batch, num_key_value_heads, n_rep, slen, head_dim
- )
- return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
-
-
-def eager_attention_forward(
- module: nn.Module,
- query: torch.Tensor,
- key: torch.Tensor,
- value: torch.Tensor,
- attention_mask: Optional[torch.Tensor],
- scaling: float,
- dropout: float = 0.0,
- **kwargs,
-):
- key_states = repeat_kv(key, module.num_key_value_groups)
- value_states = repeat_kv(value, module.num_key_value_groups)
-
- attn_weights = torch.matmul(query, key_states.transpose(2, 3)) * scaling
- if attention_mask is not None:
- causal_mask = attention_mask[:, :, :, : key_states.shape[-2]]
- attn_weights = attn_weights + causal_mask
-
- attn_weights = nn.functional.softmax(attn_weights, dim=-1, dtype=torch.float32).to(
- query.dtype
- )
- attn_weights = nn.functional.dropout(
- attn_weights, p=dropout, training=module.training
- )
- attn_output = torch.matmul(attn_weights, value_states)
- attn_output = attn_output.transpose(1, 2).contiguous()
-
- return attn_output, attn_weights
-
-
-class Qwen2Attention(nn.Module):
- """Multi-headed attention from 'Attention Is All You Need' paper"""
-
- def __init__(self, config: SuryaDecoderConfig, layer_idx: int):
- super().__init__()
- self.config = config
- self.layer_idx = layer_idx
- self.head_dim = getattr(
- config, "head_dim", config.hidden_size // config.num_attention_heads
- )
- self.num_key_value_groups = (
- config.num_attention_heads // config.num_key_value_heads
- )
- self.scaling = self.head_dim**-0.5
- self.attention_dropout = config.attention_dropout
- self.is_causal = True
- self.q_proj = nn.Linear(
- config.hidden_size, config.num_attention_heads * self.head_dim, bias=True
- )
- self.k_proj = nn.Linear(
- config.hidden_size, config.num_key_value_heads * self.head_dim, bias=True
- )
- self.v_proj = nn.Linear(
- config.hidden_size, config.num_key_value_heads * self.head_dim, bias=True
- )
- self.o_proj = nn.Linear(
- config.num_attention_heads * self.head_dim, config.hidden_size, bias=False
- )
-
- def forward(
- self,
- hidden_states: torch.Tensor,
- position_embeddings: Tuple[torch.Tensor, torch.Tensor],
- attention_mask: Optional[torch.Tensor],
- past_key_value: Optional[Cache] = None,
- cache_position: Optional[torch.LongTensor] = None,
- cache_idxs: Optional[List[int]] = None,
- num_valid_tokens: Optional[List[int]] = None,
- text_lengths: Optional[List[int]] = None,
- prefill: bool = False,
- **kwargs: Unpack[FlashAttentionKwargs],
- ) -> Tuple[torch.Tensor, Optional[torch.Tensor], Optional[Tuple[torch.Tensor]]]:
- input_shape = hidden_states.shape[:-1]
- hidden_shape = (*input_shape, -1, self.head_dim)
-
- query_states = self.q_proj(hidden_states).view(hidden_shape).transpose(1, 2)
- key_states = self.k_proj(hidden_states).view(hidden_shape).transpose(1, 2)
- value_states = self.v_proj(hidden_states).view(hidden_shape).transpose(1, 2)
-
- cos, sin = position_embeddings
- query_states, key_states = apply_rotary_pos_emb(
- query_states, key_states, cos, sin
- )
-
- if past_key_value is not None:
- # sin and cos are specific to RoPE models; cache_position needed for the static cache
- # cache_idxs, num_valid_tokens, and prefill add support for our new caching mechanism
- cache_kwargs = {
- "sin": sin,
- "cos": cos,
- "cache_position": cache_position,
- "cache_idxs": cache_idxs,
- "num_valid_tokens": num_valid_tokens,
- "prefill": prefill,
- "text_lengths": text_lengths,
- }
- key_states, value_states = past_key_value.update(
- key_states, value_states, self.layer_idx, cache_kwargs
- )
-
- attention_interface: Callable = eager_attention_forward
- if self.config._attn_implementation != "eager":
- if self.config._attn_implementation == "sdpa" and kwargs.get(
- "output_attentions", False
- ):
- logger.warning_once(
- "`torch.nn.functional.scaled_dot_product_attention` does not support `output_attentions=True`. Falling back to "
- 'eager attention. This warning can be removed using the argument `attn_implementation="eager"` when loading the model.'
- )
- elif self.config._attn_implementation == "flash_attention_2":
- # Needed for CPU -> GPU
- from surya.common.surya.flash_attn_utils import (
- flash_attn_decode,
- flash_attn_prefill,
- )
-
- if prefill:
- attention_interface = flash_attn_prefill
- else:
- attention_interface = flash_attn_decode
- else:
- attention_interface = ALL_ATTENTION_FUNCTIONS[
- self.config._attn_implementation
- ]
-
- """
- IMPORTANT:
- We sometimes use a custom sliding window impl. during training
-
- We force this to None to ensure that the HF attention integrations do not
- perform any special handling - FA2 in particular will ignore the 4D mask, and use this instead
- to infer the final mask
-
- SDPA ignores this completely, and is fully dependent on the 4D mask - (https://github.com/huggingface/transformers/blob/b9faf2f93085e3cf2c65184a69d1d9e502f95786/src/transformers/integrations/sdpa_attention.py#L23)
- """
- sliding_window = None
-
- attn_output, attn_weights = attention_interface(
- self,
- query_states,
- key_states,
- value_states,
- attention_mask,
- dropout=0.0 if not self.training else self.attention_dropout,
- scaling=self.scaling,
- sliding_window=sliding_window, # main diff with Llama
- **kwargs,
- )
-
- attn_output = attn_output.reshape(*input_shape, -1).contiguous()
- attn_output = self.o_proj(attn_output)
- return attn_output, attn_weights
-
-
-class Qwen2RMSNorm(nn.Module):
- def __init__(self, hidden_size, eps=1e-6):
- """
- Qwen2RMSNorm is equivalent to T5LayerNorm
- """
- super().__init__()
- self.weight = nn.Parameter(torch.ones(hidden_size))
- self.variance_epsilon = eps
-
- def forward(self, hidden_states):
- input_dtype = hidden_states.dtype
- hidden_states = hidden_states.to(torch.float32)
- variance = hidden_states.pow(2).mean(-1, keepdim=True)
- hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
- return self.weight * hidden_states.to(input_dtype)
-
- def extra_repr(self):
- return f"{tuple(self.weight.shape)}, eps={self.variance_epsilon}"
-
-
-class Qwen2DecoderLayer(nn.Module):
- def __init__(self, config: SuryaDecoderConfig, layer_idx: int):
- super().__init__()
- self.hidden_size = config.hidden_size
- self.self_attn = Qwen2Attention(config=config, layer_idx=layer_idx)
- self.mlp = Qwen2MLP(config)
- self.input_layernorm = Qwen2RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
- self.post_attention_layernorm = Qwen2RMSNorm(
- config.hidden_size, eps=config.rms_norm_eps
- )
-
- def forward(
- self,
- hidden_states: torch.Tensor,
- attention_mask: Optional[torch.Tensor] = None,
- position_ids: Optional[torch.LongTensor] = None,
- past_key_value: Optional[Cache] = None,
- output_attentions: Optional[bool] = False,
- use_cache: Optional[bool] = False,
- cache_position: Optional[torch.LongTensor] = None,
- cache_idxs: Optional[List[int]] = None,
- num_valid_tokens: Optional[List[int]] = None,
- text_lengths: Optional[List[int]] = None,
- prefill: bool = False,
- position_embeddings: Optional[
- Tuple[torch.Tensor, torch.Tensor]
- ] = None, # necessary, but kept here for BC
- **kwargs: Unpack[FlashAttentionKwargs],
- ) -> Tuple[
- torch.FloatTensor, Optional[Tuple[torch.FloatTensor, torch.FloatTensor]]
- ]:
- residual = hidden_states
-
- hidden_states = self.input_layernorm(hidden_states)
-
- # Self Attention
- hidden_states, self_attn_weights = self.self_attn(
- hidden_states=hidden_states,
- attention_mask=attention_mask,
- position_ids=position_ids,
- past_key_value=past_key_value,
- output_attentions=output_attentions,
- use_cache=use_cache,
- cache_position=cache_position,
- position_embeddings=position_embeddings,
- cache_idxs=cache_idxs,
- num_valid_tokens=num_valid_tokens,
- text_lengths=text_lengths,
- prefill=prefill,
- **kwargs,
- )
- hidden_states = residual + hidden_states
-
- # Fully Connected
- residual = hidden_states
- hidden_states = self.post_attention_layernorm(hidden_states)
- hidden_states = self.mlp(hidden_states)
- hidden_states = residual + hidden_states
-
- outputs = (hidden_states,)
- if output_attentions:
- outputs += (self_attn_weights,)
-
- return outputs
-
-
-class Qwen2RotaryEmbedding(nn.Module):
- def __init__(self, config: SuryaDecoderConfig, device=None):
- super().__init__()
- # BC: "rope_type" was originally "type"
- if hasattr(config, "rope_scaling") and config.rope_scaling is not None:
- self.rope_type = config.rope_scaling.get(
- "rope_type", config.rope_scaling.get("type")
- )
- else:
- self.rope_type = "default"
- self.max_seq_len_cached = config.max_position_embeddings
- self.original_max_seq_len = config.max_position_embeddings
-
- self.config = config
- self.rope_init_fn = ROPE_INIT_FUNCTIONS[self.rope_type]
-
- inv_freq, self.attention_scaling = self.rope_init_fn(self.config, device)
- self.register_buffer("inv_freq", inv_freq, persistent=False)
- self.original_inv_freq = self.inv_freq
-
- def _dynamic_frequency_update(self, position_ids, device):
- """
- dynamic RoPE layers should recompute `inv_freq` in the following situations:
- 1 - growing beyond the cached sequence length (allow scaling)
- 2 - the current sequence length is in the original scale (avoid losing precision with small sequences)
- """
- seq_len = torch.max(position_ids) + 1
- if seq_len > self.max_seq_len_cached: # growth
- inv_freq, self.attention_scaling = self.rope_init_fn(
- self.config, device, seq_len=seq_len
- )
- self.register_buffer(
- "inv_freq", inv_freq, persistent=False
- ) # TODO joao: may break with compilation
- self.max_seq_len_cached = seq_len
-
- if (
- seq_len < self.original_max_seq_len
- and self.max_seq_len_cached > self.original_max_seq_len
- ): # reset
- # This .to() is needed if the model has been moved to a device after being initialized (because
- # the buffer is automatically moved, but not the original copy)
- self.original_inv_freq = self.original_inv_freq.to(device)
- self.register_buffer("inv_freq", self.original_inv_freq, persistent=False)
- self.max_seq_len_cached = self.original_max_seq_len
-
- @torch.no_grad()
- def forward(self, x, position_ids):
- if "dynamic" in self.rope_type:
- self._dynamic_frequency_update(position_ids, device=x.device)
-
- # Core RoPE block
- inv_freq_expanded = (
- self.inv_freq[None, :, None].float().expand(position_ids.shape[0], -1, 1)
- )
- position_ids_expanded = position_ids[:, None, :].float()
- # Force float32 (see https://github.com/huggingface/transformers/pull/29285)
- device_type = x.device.type
- device_type = (
- device_type
- if isinstance(device_type, str) and device_type != "mps"
- else "cpu"
- )
- with torch.autocast(device_type=device_type, enabled=False):
- freqs = (
- inv_freq_expanded.float().to(x.device) @ position_ids_expanded.float()
- ).transpose(1, 2)
- emb = torch.cat((freqs, freqs), dim=-1)
- cos = emb.cos()
- sin = emb.sin()
-
- # Advanced RoPE types (e.g. yarn) apply a post-processing scaling factor, equivalent to scaling attention
- cos = cos * self.attention_scaling
- sin = sin * self.attention_scaling
-
- return cos.to(dtype=x.dtype), sin.to(dtype=x.dtype)
-
-
-class Qwen2PreTrainedModel(SuryaPreTrainedModel):
- config_class = SuryaDecoderConfig
- base_model_prefix = "model"
- supports_gradient_checkpointing = True
- _no_split_modules = ["Qwen2DecoderLayer"]
- _skip_keys_device_placement = ["past_key_values"]
- _supports_flash_attn_2 = True
- _supports_sdpa = True
- _supports_flex_attn = True
- _supports_cache_class = True
- _supports_quantized_cache = True
- _supports_static_cache = True
- _supports_attention_backend = True
-
- def _init_weights(self, module):
- std = self.config.initializer_range
- if isinstance(module, nn.Linear):
- module.weight.data.normal_(mean=0.0, std=std)
- if module.bias is not None:
- module.bias.data.zero_()
- elif isinstance(module, nn.Embedding):
- module.weight.data.normal_(mean=0.0, std=std)
- if module.padding_idx is not None:
- module.weight.data[module.padding_idx].zero_()
-
-
-class SuryaDecoderModel(Qwen2PreTrainedModel):
- """
- Transformer decoder consisting of *config.num_hidden_layers* layers. Each layer is a [`Qwen2DecoderLayer`]
- This variant has been modified to remove the embedding layer completely - It only supports inputs_embeds as an input
-
- Args:
- config: Qwen2Config
- """
-
- def __init__(self, config: SuryaDecoderConfig):
- super().__init__(config)
- self.padding_idx = config.pad_token_id
- self.vocab_size = config.vocab_size
-
- self.layers = nn.ModuleList(
- [
- Qwen2DecoderLayer(config, layer_idx)
- for layer_idx in range(config.num_hidden_layers)
- ]
- )
- self.norm = Qwen2RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
- self.rotary_emb = Qwen2RotaryEmbedding(config=config)
- self.gradient_checkpointing = False
-
- # Initialize weights and apply final processing
- self.post_init()
-
- def forward(
- self,
- attention_mask: Optional[torch.Tensor] = None,
- position_ids: Optional[torch.LongTensor] = None,
- past_key_values: Optional[Cache] = None,
- inputs_embeds: Optional[torch.FloatTensor] = None,
- use_cache: Optional[bool] = None,
- output_attentions: Optional[bool] = None,
- output_hidden_states: Optional[bool] = None,
- return_dict: Optional[bool] = None,
- cache_position: Optional[torch.LongTensor] = None,
- cache_idxs: Optional[List[int]] = None,
- num_valid_tokens: Optional[List[int]] = None,
- text_lengths: Optional[List[int]] = None,
- prefill: bool = False,
- **flash_attn_kwargs: Unpack[FlashAttentionKwargs],
- ) -> Union[Tuple, BaseModelOutputWithPast]:
- use_cache = use_cache if use_cache is not None else self.config.use_cache
- return_dict = (
- return_dict if return_dict is not None else self.config.use_return_dict
- )
-
- if inputs_embeds is None:
- raise ValueError("You must specify inputs_embeds")
-
- if cache_position is None:
- raise ValueError("You must specify cache_position")
-
- if position_ids is None:
- raise ValueError("You must specify position_ids")
-
- hidden_states = inputs_embeds
- causal_mask = (
- attention_mask # We make the 4D mask in the combined model when needed
- )
-
- # create position embeddings to be shared across the decoder layers
- position_embeddings = self.rotary_emb(hidden_states, position_ids)
-
- # decoder layers
- for decoder_layer in self.layers[: self.config.num_hidden_layers]:
- layer_outputs = decoder_layer(
- hidden_states,
- attention_mask=causal_mask,
- position_ids=position_ids,
- past_key_value=past_key_values,
- output_attentions=output_attentions,
- use_cache=use_cache,
- cache_position=cache_position,
- position_embeddings=position_embeddings,
- cache_idxs=cache_idxs,
- num_valid_tokens=num_valid_tokens,
- prefill=prefill,
- text_lengths=text_lengths,
- **flash_attn_kwargs,
- )
-
- hidden_states = layer_outputs[0]
-
- hidden_states = self.norm(hidden_states)
-
- output = BaseModelOutputWithPast(
- last_hidden_state=hidden_states,
- past_key_values=past_key_values if use_cache else None,
- )
- return output if return_dict else output.to_tuple()
diff --git a/surya/common/surya/decoder/config.py b/surya/common/surya/decoder/config.py
deleted file mode 100644
index b4a9bb8c..00000000
--- a/surya/common/surya/decoder/config.py
+++ /dev/null
@@ -1,83 +0,0 @@
-from transformers.configuration_utils import PretrainedConfig
-from transformers.modeling_rope_utils import rope_config_validation
-from transformers.utils import logging
-
-logger = logging.get_logger(__name__)
-
-
-class SuryaDecoderConfig(PretrainedConfig):
- model_type = "qwen2"
- keys_to_ignore_at_inference = ["past_key_values"]
-
- # Default tensor parallel plan for base model `Qwen2`
- base_model_tp_plan = {
- "layers.*.self_attn.q_proj": "colwise",
- "layers.*.self_attn.k_proj": "colwise",
- "layers.*.self_attn.v_proj": "colwise",
- "layers.*.self_attn.o_proj": "rowwise",
- "layers.*.mlp.gate_proj": "colwise",
- "layers.*.mlp.up_proj": "colwise",
- "layers.*.mlp.down_proj": "rowwise",
- }
- base_model_pp_plan = {
- "embed_tokens": (["input_ids"], ["inputs_embeds"]),
- "layers": (["hidden_states", "attention_mask"], ["hidden_states"]),
- "norm": (["hidden_states"], ["hidden_states"]),
- }
-
- def __init__(
- self,
- vocab_size=151936,
- hidden_size=4096,
- intermediate_size=22016,
- num_hidden_layers=32,
- num_attention_heads=32,
- num_key_value_heads=32,
- hidden_act="silu",
- max_position_embeddings=32768,
- initializer_range=0.02,
- rms_norm_eps=1e-6,
- use_cache=True,
- tie_word_embeddings=False,
- rope_theta=10000.0,
- rope_scaling=None,
- use_sliding_window=False,
- sliding_window=4096,
- max_window_layers=28,
- attention_dropout=0.0,
- **kwargs,
- ):
- self.vocab_size = vocab_size
- self.max_position_embeddings = max_position_embeddings
- self.hidden_size = hidden_size
- self.intermediate_size = intermediate_size
- self.num_hidden_layers = num_hidden_layers
- self.num_attention_heads = num_attention_heads
- self.use_sliding_window = False # Disable sliding window
- self.sliding_window = (
- sliding_window # we check `use_sliding_window` in the modeling code
- )
- self.max_window_layers = max_window_layers
-
- # for backward compatibility
- if num_key_value_heads is None:
- num_key_value_heads = num_attention_heads
-
- self.num_key_value_heads = num_key_value_heads
- self.hidden_act = hidden_act
- self.initializer_range = initializer_range
- self.rms_norm_eps = rms_norm_eps
- self.use_cache = use_cache
- self.rope_theta = rope_theta
- self.rope_scaling = rope_scaling
- self.attention_dropout = attention_dropout
- # Validate the correctness of rotary position embeddings parameters
- # BC: if there is a 'type' field, move it to 'rope_type'.
- if self.rope_scaling is not None and "type" in self.rope_scaling:
- self.rope_scaling["rope_type"] = self.rope_scaling["type"]
- rope_config_validation(self)
-
- super().__init__(
- tie_word_embeddings=tie_word_embeddings,
- **kwargs,
- )
diff --git a/surya/common/surya/embedder/__init__.py b/surya/common/surya/embedder/__init__.py
deleted file mode 100644
index 02d537b7..00000000
--- a/surya/common/surya/embedder/__init__.py
+++ /dev/null
@@ -1,61 +0,0 @@
-import torch
-import torch.nn as nn
-import torch.nn.functional as F
-
-
-class SimpleTokenEmbedder(nn.Module):
- def __init__(self, config):
- super().__init__()
- self.token_embed = nn.Embedding(config.vocab_size, config.hidden_size)
- self.bbox_embed = nn.ModuleList(
- [
- nn.Embedding(
- config.bbox_size + config.special_token_count,
- config.bbox_embed_size,
- )
- for _ in range(6)
- ]
- )
- self.max_bbox_embedding = config.bbox_size + config.special_token_count - 1
- self.max_bbox_size = config.bbox_size
-
- def embed(
- self,
- input_tokens: torch.Tensor,
- input_boxes: torch.Tensor | None,
- embed_boxes: torch.Tensor,
- ) -> torch.Tensor:
- # Embed tokens
- token_embeds = self.token_embed(input_tokens)
-
- # Optionally embed boxes
- if input_boxes is not None and embed_boxes.any(): # Is none in prefill
- input_boxes = input_boxes.to(torch.long)
- bbox_loss_ignore_mask = (
- (input_boxes[:, :, 0] < 0) | (input_boxes[:, :, 0] > self.max_bbox_size)
- ).unsqueeze(-1)
- input_boxes = torch.clamp(input_boxes, 0, self.max_bbox_embedding)
-
- bbox_embeds = torch.sum(
- torch.stack(
- [
- self.bbox_embed[i](input_boxes[:, :, i])
- for i in range(len(self.bbox_embed))
- ],
- dim=-1,
- ),
- dim=-1,
- )
-
- bbox_embeds = F.pad(
- bbox_embeds, (token_embeds.shape[-1] - bbox_embeds.shape[-1], 0)
- )
- embed_boxes = embed_boxes.unsqueeze(1).unsqueeze(1).expand_as(bbox_embeds)
- bbox_loss_ignore_mask = bbox_loss_ignore_mask.expand_as(bbox_embeds)
-
- mask = embed_boxes & ~bbox_loss_ignore_mask
- bbox_embeds *= mask.float()
-
- token_embeds = token_embeds + bbox_embeds
-
- return token_embeds
diff --git a/surya/common/surya/encoder/__init__.py b/surya/common/surya/encoder/__init__.py
deleted file mode 100644
index 8cbf5041..00000000
--- a/surya/common/surya/encoder/__init__.py
+++ /dev/null
@@ -1,801 +0,0 @@
-import math
-from typing import Optional, Tuple
-
-import torch
-import torch.nn as nn
-import torch.nn.functional as F
-from transformers.activations import ACT2FN
-
-from surya.common.pretrained import SuryaPreTrainedModel
-from surya.common.surya.encoder.config import SuryaEncoderConfig
-from surya.common.xla import get_nearest_pad
-from surya.logging import get_logger
-from surya.settings import settings
-
-if settings.FOUNDATION_XLA:
- import torch_xla.experimental.custom_kernel
-
-from surya.logging import get_logger
-logger = get_logger()
-
-
-class Qwen2_5_VLMLP(nn.Module):
- def __init__(self, config, bias: bool = False):
- super().__init__()
- self.hidden_size = config.hidden_size
- self.intermediate_size = config.intermediate_size
- self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=bias)
- self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=bias)
- self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=bias)
- self.act_fn = ACT2FN[config.hidden_act]
-
- def forward(self, hidden_state):
- return self.down_proj(
- self.act_fn(self.gate_proj(hidden_state)) * self.up_proj(hidden_state)
- )
-
-
-class Qwen2_5_VisionPatchEmbed(nn.Module):
- def __init__(
- self,
- patch_size: int = 14,
- temporal_patch_size: int = 2,
- in_channels: int = 3,
- embed_dim: int = 1152,
- ) -> None:
- super().__init__()
- self.patch_size = patch_size
- self.temporal_patch_size = temporal_patch_size
- self.in_channels = in_channels
- self.embed_dim = embed_dim
-
- kernel_size = [temporal_patch_size, patch_size, patch_size]
- self.proj = nn.Conv3d(
- in_channels,
- embed_dim,
- kernel_size=kernel_size,
- stride=kernel_size,
- bias=False,
- )
-
- def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
- target_dtype = self.proj.weight.dtype
- bsz = hidden_states.shape[0]
- hidden_states = hidden_states.view(
- -1,
- self.in_channels,
- self.temporal_patch_size,
- self.patch_size,
- self.patch_size,
- )
- hidden_states = self.proj(hidden_states.to(dtype=target_dtype)).view(
- bsz, -1, self.embed_dim
- )
- return hidden_states
-
-
-class Qwen2_5_VisionRotaryEmbedding(nn.Module):
- def __init__(self, dim: int, theta: float = 10000.0) -> None:
- super().__init__()
- self.inv_freq = 1.0 / (
- theta ** (torch.arange(0, dim, 2, dtype=torch.float) / dim)
- )
-
- def forward(self, seqlen: int) -> torch.Tensor:
- seq = torch.arange(seqlen, device="cpu", dtype=self.inv_freq.dtype)
- freqs = torch.outer(seq, self.inv_freq)
- return freqs
-
-
-class Qwen2RMSNorm(nn.Module):
- def __init__(self, hidden_size, eps=1e-6):
- """
- Qwen2RMSNorm is equivalent to T5LayerNorm
- """
- super().__init__()
- self.weight = nn.Parameter(torch.ones(hidden_size))
- self.variance_epsilon = eps
-
- def forward(self, hidden_states):
- input_dtype = hidden_states.dtype
- hidden_states = hidden_states.to(torch.float32)
- variance = hidden_states.pow(2).mean(-1, keepdim=True)
- hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
- return self.weight * hidden_states.to(input_dtype)
-
- def extra_repr(self):
- return f"{tuple(self.weight.shape)}, eps={self.variance_epsilon}"
-
-
-class Qwen2_5_VLPatchMerger(nn.Module):
- def __init__(self, dim: int, context_dim: int, spatial_merge_size: int = 2) -> None:
- super().__init__()
- self.hidden_size = context_dim * (spatial_merge_size**2)
- self.ln_q = Qwen2RMSNorm(context_dim, eps=1e-6)
- self.mlp = nn.Sequential(
- nn.Linear(self.hidden_size, self.hidden_size),
- nn.GELU(),
- nn.Linear(self.hidden_size, dim),
- )
-
- def forward(self, x: torch.Tensor) -> torch.Tensor:
- bsz = x.shape[0]
- x = self.mlp(self.ln_q(x).view(bsz, -1, self.hidden_size))
- return x
-
-
-def apply_rotary_pos_emb_flashatt(
- q: torch.Tensor, k: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor
-) -> Tuple[torch.Tensor, torch.Tensor]:
- from flash_attn.layers.rotary import apply_rotary_emb
-
- cos = cos.chunk(2, dim=-1)[0].contiguous()
- sin = sin.chunk(2, dim=-1)[0].contiguous()
- q_embed = apply_rotary_emb(q.float(), cos.float(), sin.float()).type_as(q)
- k_embed = apply_rotary_emb(k.float(), cos.float(), sin.float()).type_as(k)
- return q_embed, k_embed
-
-
-class Qwen2_5_VLVisionXLASdpaAttention(nn.Module):
- def __init__(self, dim: int, num_heads: int = 16) -> None:
- super().__init__()
- self.num_heads = num_heads
- self.qkv = nn.Linear(dim, dim * 3, bias=True)
- self.proj = nn.Linear(dim, dim)
- self.head_dim = dim // num_heads
-
- def forward(
- self,
- hidden_states: torch.Tensor,
- cu_seqlens: torch.Tensor,
- rotary_pos_emb: Optional[torch.Tensor] = None,
- position_embeddings: Optional[Tuple[torch.Tensor, torch.Tensor]] = None,
- ) -> torch.Tensor:
- bsz, seq_length = hidden_states.shape[0], hidden_states.shape[1]
- q, k, v = (
- self.qkv(hidden_states)
- .reshape(bsz, seq_length, 3, self.num_heads, -1)
- .permute(0, 2, 1, 3, 4)
- .unbind(1)
- )
- if position_embeddings is None:
- logger.warning_once(
- "The attention layers in this model are transitioning from computing the RoPE embeddings internally "
- "through `rotary_pos_emb` (2D tensor of RoPE theta values), to using externally computed "
- "`position_embeddings` (Tuple of tensors, containing cos and sin). In v4.54 `rotary_pos_emb` will be "
- "removed and `position_embeddings` will be mandatory."
- )
- emb = torch.cat((rotary_pos_emb, rotary_pos_emb), dim=-1)
- cos = emb.cos()
- sin = emb.sin()
- else:
- cos, sin = position_embeddings
- q, k = apply_rotary_pos_emb_vision(q, k, cos, sin)
-
- attention_mask = torch.zeros([bsz, 1, seq_length, seq_length], dtype=torch.bool)
- cu_seqlens_cpu = cu_seqlens.cpu()
- for j in range(bsz):
- batch_seqlens = cu_seqlens_cpu[j]
- for i in range(1, len(batch_seqlens)):
- attention_mask[
- j,
- ...,
- batch_seqlens[i - 1] : batch_seqlens[i],
- batch_seqlens[i - 1] : batch_seqlens[i],
- ] = True
-
- attention_mask = attention_mask.to(q.device)
-
- q = q.transpose(1, 2)
- k = k.transpose(1, 2)
- v = v.transpose(1, 2)
-
- attn_output = F.scaled_dot_product_attention(
- q,
- k,
- v,
- attention_mask,
- dropout_p=0.0,
- )
- attn_output = attn_output.transpose(1, 2)
- attn_output = attn_output.reshape(bsz, seq_length, -1)
- attn_output = self.proj(attn_output)
- return attn_output
-
-
-class Qwen2_5_VLVisionXLAFlashAttention2(nn.Module):
- def __init__(self, dim: int, num_heads: int = 16) -> None:
- super().__init__()
- self.num_heads = num_heads
- self.qkv = nn.Linear(dim, dim * 3, bias=True)
- self.proj = nn.Linear(dim, dim)
- self.head_dim = dim // num_heads
-
- def forward(
- self,
- hidden_states: torch.Tensor,
- cu_seqlens: torch.Tensor,
- rotary_pos_emb: Optional[torch.Tensor] = None,
- position_embeddings: Optional[Tuple[torch.Tensor, torch.Tensor]] = None,
- ) -> torch.Tensor:
- # Note, this is faster than SDPA, but pretty memory inefficient
- # It also has significant accuracy issues
-
- bsz, seq_length = hidden_states.shape[0], hidden_states.shape[1]
-
- # Single reshape to target layout - avoid multiple operations
- q, k, v = (
- self.qkv(hidden_states)
- .reshape(bsz, seq_length, 3, self.num_heads, -1)
- .permute(0, 2, 1, 3, 4)
- .unbind(1)
- )
-
- # Apply rotary embeddings if provided
- if position_embeddings is not None:
- cos, sin = position_embeddings
- q, k = apply_rotary_pos_emb_vision(q, k, cos, sin)
-
- # Single reshape to flash attention format [batch, num_heads, seq_len, head_dim]
- q = q.transpose(1, 2) # [bsz, num_heads, seq_len, head_dim]
- k = k.transpose(1, 2)
- v = v.transpose(1, 2)
-
- total_seqlen = q.shape[2]
- # from cu_seqlens to segment ids for each position in dim 0
- additive_bias = torch.zeros((bsz, 1, total_seqlen, total_seqlen), dtype=q.dtype)
- min_val = torch.finfo(q.dtype).min
-
- for i in range(bsz):
- padding_end = cu_seqlens[i][1].item()
- additive_bias[i, :, :, :padding_end] = min_val
-
- additive_bias = additive_bias.to(hidden_states.device)
-
- attn_scale = 1 / math.sqrt(self.head_dim)
- attn_output = torch_xla.experimental.custom_kernel.flash_attention(
- q, k, v, sm_scale=attn_scale, ab=additive_bias
- )
- attn_output = (
- attn_output.transpose(1, 2).contiguous().reshape(bsz, seq_length, -1)
- )
- attn_output = self.proj(attn_output)
- return attn_output
-
-
-class Qwen2_5_VLVisionFlashAttention2(nn.Module):
- def __init__(self, dim: int, num_heads: int = 16) -> None:
- super().__init__()
- self.num_heads = num_heads
- self.qkv = nn.Linear(dim, dim * 3, bias=True)
- self.proj = nn.Linear(dim, dim)
-
- def forward(
- self,
- hidden_states: torch.Tensor,
- cu_seqlens: torch.Tensor,
- rotary_pos_emb: Optional[torch.Tensor] = None,
- position_embeddings: Optional[Tuple[torch.Tensor, torch.Tensor]] = None,
- ) -> torch.Tensor:
- from flash_attn import flash_attn_varlen_func
-
- bsz = hidden_states.shape[0]
- seq_length = hidden_states.shape[1]
- q, k, v = (
- self.qkv(hidden_states)
- .reshape(bsz, seq_length, 3, self.num_heads, -1)
- .permute(0, 2, 1, 3, 4)
- .unbind(1)
- )
- if position_embeddings is None:
- logger.warning_once(
- "The attention layers in this model are transitioning from computing the RoPE embeddings internally "
- "through `rotary_pos_emb` (2D tensor of RoPE theta values), to using externally computed "
- "`position_embeddings` (Tuple of tensors, containing cos and sin). In v4.54 `rotary_pos_emb` will be "
- "removed and `position_embeddings` will be mandatory."
- )
- emb = torch.cat((rotary_pos_emb, rotary_pos_emb), dim=-1)
- cos = emb.cos()
- sin = emb.sin()
- else:
- cos, sin = position_embeddings
-
- q, k = apply_rotary_pos_emb_flashatt(q, k, cos.squeeze(0), sin.squeeze(0))
-
- q = q.squeeze(0)
- k = k.squeeze(0)
- v = v.squeeze(0)
- cu_seqlens = cu_seqlens.squeeze(0)
-
- max_seqlen = (cu_seqlens[1:] - cu_seqlens[:-1]).max().item()
- attn_output = flash_attn_varlen_func(
- q, k, v, cu_seqlens, cu_seqlens, max_seqlen, max_seqlen
- ).reshape(bsz, seq_length, -1)
- attn_output = self.proj(attn_output)
- return attn_output
-
-
-def rotate_half(x):
- """Rotates half the hidden dims of the input."""
- x1 = x[..., : x.shape[-1] // 2]
- x2 = x[..., x.shape[-1] // 2 :]
- return torch.cat((-x2, x1), dim=-1)
-
-
-def apply_rotary_pos_emb_vision(
- q: torch.Tensor, k: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor
-) -> Tuple[torch.Tensor, torch.Tensor]:
- orig_q_dtype = q.dtype
- orig_k_dtype = k.dtype
- q, k = q.float(), k.float()
- cos, sin = cos.unsqueeze(-2).float(), sin.unsqueeze(-2).float()
- q_embed = (q * cos) + (rotate_half(q) * sin)
- k_embed = (k * cos) + (rotate_half(k) * sin)
- q_embed = q_embed.to(orig_q_dtype)
- k_embed = k_embed.to(orig_k_dtype)
- return q_embed, k_embed
-
-
-class Qwen2_5_VLVisionAttention(nn.Module):
- def __init__(self, dim: int, num_heads: int = 16) -> None:
- super().__init__()
- self.num_heads = num_heads
- self.head_dim = dim // num_heads
- self.qkv = nn.Linear(dim, dim * 3, bias=True)
- self.proj = nn.Linear(dim, dim)
-
- def forward(
- self,
- hidden_states: torch.Tensor,
- cu_seqlens: torch.Tensor,
- rotary_pos_emb: Optional[torch.Tensor] = None,
- position_embeddings: Optional[Tuple[torch.Tensor, torch.Tensor]] = None,
- ) -> torch.Tensor:
- bsz, seq_length = hidden_states.shape[0], hidden_states.shape[1]
- q, k, v = (
- self.qkv(hidden_states)
- .reshape(bsz, seq_length, 3, self.num_heads, -1)
- .permute(0, 2, 1, 3, 4)
- .unbind(1)
- )
- if position_embeddings is None:
- logger.warning_once(
- "The attention layers in this model are transitioning from computing the RoPE embeddings internally "
- "through `rotary_pos_emb` (2D tensor of RoPE theta values), to using externally computed "
- "`position_embeddings` (Tuple of tensors, containing cos and sin). In v4.54 `rotary_pos_emb` will be "
- "removed and `position_embeddings` will be mandatory."
- )
- emb = torch.cat((rotary_pos_emb, rotary_pos_emb), dim=-1)
- cos = emb.cos()
- sin = emb.sin()
- else:
- cos, sin = position_embeddings
-
- q, k = apply_rotary_pos_emb_vision(q, k, cos, sin)
-
- attention_mask = torch.full(
- [bsz, 1, seq_length, seq_length],
- torch.finfo(q.dtype).min,
- device=q.device,
- dtype=q.dtype,
- )
- for j in range(bsz):
- batch_seqlens = cu_seqlens[j]
- for i in range(1, len(batch_seqlens)):
- attention_mask[
- j,
- ...,
- batch_seqlens[i - 1] : batch_seqlens[i],
- batch_seqlens[i - 1] : batch_seqlens[i],
- ] = 0
-
- q = q.transpose(1, 2)
- k = k.transpose(1, 2)
- v = v.transpose(1, 2)
-
- attn_weights = torch.matmul(q, k.transpose(2, 3)) / math.sqrt(self.head_dim)
- attn_weights = attn_weights + attention_mask
- attn_weights = nn.functional.softmax(
- attn_weights, dim=-1, dtype=torch.float32
- ).to(q.dtype)
- attn_output = torch.matmul(attn_weights, v)
- attn_output = attn_output.transpose(1, 2)
- attn_output = attn_output.reshape(bsz, seq_length, -1)
- attn_output = self.proj(attn_output)
- return attn_output
-
-
-class Qwen2_5_VLVisionSdpaAttention(nn.Module):
- def __init__(self, dim: int, num_heads: int = 16) -> None:
- super().__init__()
- self.num_heads = num_heads
- self.qkv = nn.Linear(dim, dim * 3, bias=True)
- self.proj = nn.Linear(dim, dim)
-
- def unpack_qkv_with_mask(self, q, k, v, cu_seqlens):
- """
- Unpacks q, k, v sequences into batch-major form and constructs an additive attention mask.
-
- Args:
- q, k, v: Tensors of shape (total_seq_len, num_heads, head_dim)
- cu_seqlens: Tensor of shape (batch_size + 1,) with cumulative sequence lengths
-
- Returns:
- batched_q: Tensor of shape (batch_size, max_seq_len, num_heads, head_dim)
- batched_k: Tensor of shape (batch_size, max_seq_len, num_heads, head_dim)
- batched_v: Tensor of shape (batch_size, max_seq_len, num_heads, head_dim)
- attention_mask: Tensor of shape (batch_size, 1, max_seq_len, max_seq_len)
- with 0 for valid tokens and -inf for padding (for additive attention)
- """
- device = q.device
- dtype = q.dtype
-
- batch_size = cu_seqlens.shape[0] - 1
- num_heads = q.shape[1]
- head_dim = q.shape[2]
-
- seq_lengths = cu_seqlens[1:] - cu_seqlens[:-1] # Keep as tensor
- max_seq_len = seq_lengths.max().item() # Use .max() on tensor
-
- if settings.FOUNDATION_STATIC_CACHE:
- # Pad max_seq_len to the nearest multiple for compilation
- max_seq_len = get_nearest_pad(max_seq_len, pad_multiple=16)
-
- # Pad batch_size to the nearest multiple for compilation
- batch_size = get_nearest_pad(batch_size, pad_multiple=2)
-
- # Ensure seq_lengths is a tensor of the correct size
- seq_lengths = F.pad(
- seq_lengths, (0, batch_size - seq_lengths.size(0)), "constant", 0
- )
-
- # some day, you may look at this, and think: "what if I used repeat_interlave or some other fancy torch instead"?
- # don't do this - it's a path to madness. For some reason, this loop is optimal
-
- batch_indices = []
- position_indices = []
-
- for i, seq_len in enumerate(
- seq_lengths.tolist()
- ): # Convert to list only for iteration
- batch_indices.extend([i] * seq_len)
- position_indices.extend(list(range(seq_len)))
-
- batch_indices = torch.tensor(batch_indices, device=device)
- position_indices = torch.tensor(position_indices, device=device)
-
- batched_q = torch.zeros(
- (batch_size, max_seq_len, num_heads, head_dim), device=device, dtype=dtype
- )
- batched_k = torch.zeros_like(batched_q)
- batched_v = torch.zeros_like(batched_q)
-
- # Create additive attention mask
- attention_mask = torch.full(
- (batch_size, max_seq_len, max_seq_len),
- fill_value=float("-inf"),
- device=device,
- dtype=dtype,
- )
-
- # Create mask for valid positions
- seq_range = torch.arange(max_seq_len, device=device)
- valid_mask = seq_range.unsqueeze(0) < seq_lengths.unsqueeze(
- 1
- ) # (batch_size, max_seq_len)
- valid_2d = valid_mask.unsqueeze(2) & valid_mask.unsqueeze(
- 1
- ) # (batch_size, max_seq_len, max_seq_len)
-
- # Simply use boolean indexing to set valid positions to 0
- attention_mask[valid_2d] = 0
-
- attention_mask = attention_mask.unsqueeze(
- 1
- ) # (batch_size, 1, max_seq_len, max_seq_len)
-
- batched_q[batch_indices, position_indices] = q
- batched_k[batch_indices, position_indices] = k
- batched_v[batch_indices, position_indices] = v
-
- return (
- batched_q,
- batched_k,
- batched_v,
- attention_mask,
- batch_indices,
- position_indices,
- )
-
- def forward(
- self,
- hidden_states: torch.Tensor,
- cu_seqlens: torch.Tensor,
- rotary_pos_emb: Optional[torch.Tensor] = None,
- position_embeddings: Optional[Tuple[torch.Tensor, torch.Tensor]] = None,
- ) -> torch.Tensor:
- hidden_states = hidden_states.squeeze(0)
- cu_seqlens = cu_seqlens.squeeze(0)
-
- seq_length = hidden_states.shape[0]
- q, k, v = (
- self.qkv(hidden_states)
- .reshape(seq_length, 3, self.num_heads, -1)
- .permute(1, 0, 2, 3)
- .unbind(0)
- )
- if position_embeddings is None:
- logger.warning_once(
- "The attention layers in this model are transitioning from computing the RoPE embeddings internally "
- "through `rotary_pos_emb` (2D tensor of RoPE theta values), to using externally computed "
- "`position_embeddings` (Tuple of tensors, containing cos and sin). In v4.54 `rotary_pos_emb` will be "
- "removed and `position_embeddings` will be mandatory."
- )
- emb = torch.cat((rotary_pos_emb, rotary_pos_emb), dim=-1)
- cos = emb.cos()
- sin = emb.sin()
- else:
- cos, sin = position_embeddings
- q, k = apply_rotary_pos_emb_vision(q, k, cos, sin)
- q = q.squeeze(0)
- k = k.squeeze(0)
-
- q, k, v, attention_mask, batch_indices, position_indices = (
- self.unpack_qkv_with_mask(q, k, v, cu_seqlens)
- )
- batch_size, max_seqlen = q.shape[:2]
- q = q.transpose(1, 2)
- k = k.transpose(1, 2)
- v = v.transpose(1, 2)
-
- attn_output = F.scaled_dot_product_attention(
- q,
- k,
- v,
- attention_mask,
- dropout_p=0.0,
- )
- attn_output = attn_output.permute(0, 2, 1, 3).reshape(
- batch_size, max_seqlen, -1
- ) # Bring back to (batch_size, max_seqlen, hidden_dim)
- attn_output = attn_output[batch_indices, position_indices]
- attn_output = self.proj(attn_output)
-
- return attn_output.unsqueeze(0)
-
-
-QWEN2_5_VL_VISION_ATTENTION_CLASSES = {
- "eager": Qwen2_5_VLVisionAttention,
- "flash_attention_2": Qwen2_5_VLVisionXLAFlashAttention2
- if settings.FOUNDATION_XLA
- else Qwen2_5_VLVisionFlashAttention2,
- "sdpa": Qwen2_5_VLVisionXLASdpaAttention
- if settings.FOUNDATION_XLA
- else Qwen2_5_VLVisionSdpaAttention,
-}
-
-
-class Qwen2_5_VLVisionBlock(nn.Module):
- def __init__(self, config, attn_implementation: str = "sdpa") -> None:
- super().__init__()
- self.norm1 = Qwen2RMSNorm(config.hidden_size, eps=1e-6)
- self.norm2 = Qwen2RMSNorm(config.hidden_size, eps=1e-6)
- self.attn = QWEN2_5_VL_VISION_ATTENTION_CLASSES[attn_implementation](
- config.hidden_size, num_heads=config.num_heads
- )
- self.mlp = Qwen2_5_VLMLP(config, bias=True)
-
- def forward(
- self,
- hidden_states: torch.Tensor,
- cu_seqlens: torch.Tensor,
- rotary_pos_emb: Optional[torch.Tensor] = None,
- position_embeddings: Optional[Tuple[torch.Tensor, torch.Tensor]] = None,
- ) -> torch.Tensor:
- hidden_states = hidden_states + self.attn(
- self.norm1(hidden_states),
- cu_seqlens=cu_seqlens,
- rotary_pos_emb=rotary_pos_emb,
- position_embeddings=position_embeddings,
- )
- hidden_states = hidden_states + self.mlp(self.norm2(hidden_states))
- return hidden_states
-
-
-Qwen2_5_VL_START_DOCSTRING = r"""
- This model inherits from [`PreTrainedModel`]. Check the superclass documentation for the generic methods the
- library implements for all its model (such as downloading or saving, resizing the input embeddings, pruning heads
- etc.)
-
- This model is also a PyTorch [torch.nn.Module](https://pytorch.org/docs/stable/nn.html#torch.nn.Module) subclass.
- Use it as a regular PyTorch Module and refer to the PyTorch documentation for all matter related to general usage
- and behavior.
-
- Parameters:
- config ([`Qwen2_5_VLConfig`]):
- Model configuration class with all the parameters of the model. Initializing with a config file does not
- load the weights associated with the model, only the configuration. Check out the
- [`~PreTrainedModel.from_pretrained`] method to load the model weights.
-"""
-
-
-class Qwen2_5_VLPreTrainedModel(SuryaPreTrainedModel):
- config_class = SuryaEncoderConfig
- base_model_prefix = "model"
- supports_gradient_checkpointing = True
- _no_split_modules = ["Qwen2_5_VLDecoderLayer", "Qwen2_5_VLVisionBlock"]
- _skip_keys_device_placement = "past_key_values"
- _supports_flash_attn_2 = True
- _supports_sdpa = True
- _supports_cache_class = True
- _supports_static_cache = False # TODO (joao): fix. torch.compile failing probably due to `cache_positions`
-
- def _init_weights(self, module):
- std = self.config.initializer_range
- if isinstance(module, (nn.Linear, nn.Conv3d)):
- module.weight.data.normal_(mean=0.0, std=std)
- if module.bias is not None:
- module.bias.data.zero_()
- elif isinstance(module, nn.Embedding):
- module.weight.data.normal_(mean=0.0, std=std)
- if module.padding_idx is not None:
- module.weight.data[module.padding_idx].zero_()
-
-
-class Qwen2_5_VisionTransformerPretrainedModel(Qwen2_5_VLPreTrainedModel):
- config_class = SuryaEncoderConfig
- _no_split_modules = ["Qwen2_5_VLVisionBlock"]
-
- def __init__(self, config, *inputs, **kwargs) -> None:
- super().__init__(config, *inputs, **kwargs)
- self.spatial_merge_size = config.spatial_merge_size
- self.patch_size = config.patch_size
- self.fullatt_block_indexes = config.fullatt_block_indexes
- self.window_size = config.window_size
- self.spatial_merge_unit = self.spatial_merge_size * self.spatial_merge_size
-
- self.patch_embed = Qwen2_5_VisionPatchEmbed(
- patch_size=config.patch_size,
- temporal_patch_size=config.temporal_patch_size,
- in_channels=config.in_channels,
- embed_dim=config.hidden_size,
- )
-
- head_dim = config.hidden_size // config.num_heads
- self.rotary_pos_emb = Qwen2_5_VisionRotaryEmbedding(head_dim // 2)
-
- self.blocks = nn.ModuleList(
- [
- Qwen2_5_VLVisionBlock(config, config._attn_implementation)
- for _ in range(config.depth)
- ]
- )
- self.merger = Qwen2_5_VLPatchMerger(
- dim=config.out_hidden_size,
- context_dim=config.hidden_size,
- spatial_merge_size=config.spatial_merge_size,
- )
- self.gradient_checkpointing = False
-
- def rot_pos_emb(self, grid_thw):
- rotary_pos_emb = []
- grid_thw_list = grid_thw.cpu().tolist()
- for batch_item in grid_thw_list:
- row_pos_ids = []
- heights = [h for _, h, _ in batch_item]
- widths = [w for _, _, w in batch_item]
- max_grid_size = max(heights + widths)
- for t, h, w in batch_item:
- hpos_ids = torch.arange(h).unsqueeze(1).expand(-1, w)
- hpos_ids = hpos_ids.reshape(
- h // self.spatial_merge_size,
- self.spatial_merge_size,
- w // self.spatial_merge_size,
- self.spatial_merge_size,
- )
- hpos_ids = hpos_ids.permute(0, 2, 1, 3)
- hpos_ids = hpos_ids.flatten()
-
- wpos_ids = torch.arange(w).unsqueeze(0).expand(h, -1)
- wpos_ids = wpos_ids.reshape(
- h // self.spatial_merge_size,
- self.spatial_merge_size,
- w // self.spatial_merge_size,
- self.spatial_merge_size,
- )
- wpos_ids = wpos_ids.permute(0, 2, 1, 3)
- wpos_ids = wpos_ids.flatten()
- # shape: token_count, 2
- row_pos_ids.append(
- torch.stack([hpos_ids, wpos_ids], dim=-1).repeat(t, 1)
- )
- # shape: token_count, 2
- pos_ids = torch.cat(row_pos_ids, dim=0)
- rotary_pos_emb_full = self.rotary_pos_emb(max_grid_size)
- rotary_pos_emb_row = rotary_pos_emb_full[pos_ids].flatten(1)
- rotary_pos_emb.append(rotary_pos_emb_row)
- rotary_pos_emb = torch.stack(rotary_pos_emb, dim=0)
- return rotary_pos_emb
-
- def forward(
- self,
- hidden_states: torch.Tensor,
- grid_thw: torch.Tensor,
- ) -> torch.Tensor:
- """
- Args:
- hidden_states (`torch.Tensor` of shape `(bsz, seq_len, hidden_size)`):
- The final hidden states of the model.
- grid_thw (`torch.Tensor` of shape `(bsz, num_images_or_videos, 3)`):
- The temporal, height and width of feature shape of each image in LLM.
-
- Returns:
- `torch.Tensor`: hidden_states.
- """
- bsz, seq_len, _ = hidden_states.size()
- hidden_states = self.patch_embed(hidden_states) # (bsz, seq_len, hidden_dim)
- rotary_pos_emb = self.rot_pos_emb(grid_thw)
-
- # hidden_states = hidden_states.reshape(bsz, seq_len, -1)
- # rotary_pos_emb = rotary_pos_emb.reshape(bsz, seq_len, -1)
- emb = torch.cat((rotary_pos_emb, rotary_pos_emb), dim=-1).to(
- hidden_states.device
- )
- position_embeddings = (emb.cos(), emb.sin())
-
- cu_seqlens = (grid_thw[:, :, 1] * grid_thw[:, :, 2]).cumsum(
- dim=1,
- # Select dtype based on the following factors:
- # - FA2 requires that cu_seqlens_q must have dtype int32
- # - torch.onnx.export requires that cu_seqlens_q must have same dtype as grid_thw
- # See https://github.com/huggingface/transformers/pull/34852 for more information
- dtype=grid_thw.dtype if torch.jit.is_tracing() else torch.int32,
- )
- cu_seqlens = F.pad(cu_seqlens, (1, 0), value=0)
- for layer_num, blk in enumerate(self.blocks):
- if self.gradient_checkpointing and self.training:
- hidden_states = self._gradient_checkpointing_func(
- blk.__call__,
- hidden_states,
- cu_seqlens,
- None,
- position_embeddings,
- )
- else:
- hidden_states = blk(
- hidden_states,
- cu_seqlens=cu_seqlens,
- position_embeddings=position_embeddings,
- )
-
- hidden_states = self.merger(hidden_states)
- return hidden_states
-
-
-class SuryaEncoderModel(Qwen2_5_VisionTransformerPretrainedModel):
- @property
- def image_size(self) -> int:
- config: SuryaEncoderConfig = self.config
- if isinstance(config.image_size, tuple) and len(config.image_size) == 2:
- return config.image_size
- elif isinstance(config.image_size, int):
- return (config.image_size, config.image_size)
-
- raise ValueError(
- f"The `image_size` for SwinConfig should be a tuple of (int, int) or a single int but found {type(config.image_size)}"
- )
-
- @property
- def hidden_size(self) -> int:
- config: SuryaEncoderConfig = self.config
- return config.hidden_size
-
- def embed_images(
- self,
- image_batch: torch.Tensor,
- grid_thw: torch.Tensor,
- ) -> torch.Tensor:
- return super().forward(
- hidden_states=image_batch,
- grid_thw=grid_thw,
- )
diff --git a/surya/common/surya/encoder/config.py b/surya/common/surya/encoder/config.py
deleted file mode 100644
index 343fbee6..00000000
--- a/surya/common/surya/encoder/config.py
+++ /dev/null
@@ -1,53 +0,0 @@
-from transformers.configuration_utils import PretrainedConfig
-from transformers.utils import logging
-
-logger = logging.get_logger(__name__)
-
-
-class SuryaEncoderConfig(PretrainedConfig):
- model_type = "qwen2_5_vl"
- base_config_key = "vision_config"
-
- attribute_map = {
- "num_attention_heads": "num_heads",
- "num_hidden_layers": "depth",
- }
-
- def __init__(
- self,
- depth=8,
- hidden_size=1280,
- hidden_act="silu",
- intermediate_size=3420,
- num_heads=16,
- in_channels=3,
- patch_size=14,
- spatial_merge_size=2,
- spatial_patch_size=14,
- temporal_patch_size=1,
- tokens_per_second=4,
- window_size=112,
- out_hidden_size=1280,
- fullatt_block_indexes=(3, 7),
- initializer_range=0.02,
- image_size=4096,
- **kwargs,
- ):
- super().__init__(**kwargs)
-
- self.depth = depth
- self.hidden_size = hidden_size
- self.hidden_act = hidden_act
- self.intermediate_size = intermediate_size
- self.num_heads = num_heads
- self.in_channels = in_channels
- self.patch_size = patch_size
- self.spatial_merge_size = spatial_merge_size
- self.temporal_patch_size = temporal_patch_size
- self.tokens_per_second = tokens_per_second
- self.window_size = window_size
- self.fullatt_block_indexes = fullatt_block_indexes
- self.out_hidden_size = out_hidden_size
- self.initializer_range = initializer_range
- self.spatial_patch_size = spatial_patch_size
- self.image_size = image_size
diff --git a/surya/common/surya/flash_attn_utils.py b/surya/common/surya/flash_attn_utils.py
deleted file mode 100644
index c8aefe35..00000000
--- a/surya/common/surya/flash_attn_utils.py
+++ /dev/null
@@ -1,196 +0,0 @@
-from typing import Optional
-import torch
-import torch.nn.functional as F
-from flash_attn import flash_attn_varlen_func as _flash_attn_varlen_func
-from flash_attn import flash_attn_with_kvcache as _flash_attn_with_kvcache
-from flash_attn.bert_padding import index_first_axis as _index_first_axis
-from flash_attn.bert_padding import pad_input
-
-def _get_unpad_data(attention_mask: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor, int]:
- """
- Retrieves indexing data required to repad unpadded (ragged) tensors.
-
- Arguments:
- attention_mask (`torch.Tensor`):
- Boolean or int tensor of shape (batch_size, sequence_length), 1 means valid and 0 means not valid.
-
- Return:
- indices (`torch.Tensor`):
- The indices of non-masked tokens from the flattened input sequence.
- cu_seqlens (`torch.Tensor`):
- The cumulative sequence lengths, used to index into ragged (unpadded) tensors. `cu_seqlens` shape is (batch_size + 1,).
- max_seqlen_in_batch (`int`):
- Maximum sequence length in batch.
- """
- seqlens_in_batch = attention_mask.sum(dim=-1, dtype=torch.int32)
- indices = torch.nonzero(attention_mask.flatten(), as_tuple=False).flatten()
- max_seqlen_in_batch = seqlens_in_batch.max().item()
- cu_seqlens = F.pad(torch.cumsum(seqlens_in_batch, dim=0, dtype=torch.int32), (1, 0))
- return (
- indices,
- cu_seqlens,
- max_seqlen_in_batch,
- )
-
-def _upad_input(
- query_layer: torch.Tensor,
- key_layer: torch.Tensor,
- value_layer: torch.Tensor,
- query_length: int,
- indices_k,
- cu_seqlens_k,
- max_seqlen_in_batch_k
-):
- """
- Unpads query, key, and values tensors, using a single dimension for all tokens even though they belong to different batches.
-
- This function is used instead of `flash_attn.bert_padding.unpad_input` in order to avoid the recomputation of the same intermediary
- tensors for query, key, value tensors.
-
- Arguments:
- query_layer (`torch.Tensor`):
- Query state with padding. Shape: (batch_size, query_length, num_heads, head_dim).
- key_layer (`torch.Tensor`):
- Key state with padding. Shape: (batch_size, kv_seq_len, num_key_value_heads, head_dim).
- value_layer (`torch.Tensor`):
- Value state with padding. Shape: (batch_size, kv_seq_len, num_key_value_heads, head_dim).
- attention_mask (`torch.Tensor`):
- Boolean or int tensor of shape (batch_size, sequence_length), 1 means valid and 0 means not valid.
- query_length (`int`):
- Target length.
-
- Return:
- query_layer (`torch.Tensor`):
- Query state without padding. Shape: (total_target_length, num_heads, head_dim).
- key_layer (`torch.Tensor`):
- Key state with padding. Shape: (total_source_length, num_key_value_heads, head_dim).
- value_layer (`torch.Tensor`):
- Value state with padding. Shape: (total_source_length, num_key_value_heads, head_dim).
- indices_q (`torch.Tensor`):
- The indices of non-masked tokens from the flattened input target sequence.
- (cu_seqlens_q, cu_seqlens_k) (`Tuple[int]`):
- The cumulative sequence lengths for the target (query) and source (key, value), used to index into ragged (unpadded) tensors. `cu_seqlens` shape is (batch_size + 1,).
- (max_seqlen_in_batch_q, max_seqlen_in_batch_k) (`Tuple[int]`):
- Maximum sequence length in batch (`max_seqlen_in_batch_q` for the target sequence i.e. query, `max_seqlen_in_batch_k` for the source sequence i.e. key/value).
- """
- batch_size, kv_seq_len, num_key_value_heads, head_dim = key_layer.shape
-
- key_layer = _index_first_axis(key_layer.reshape(batch_size * kv_seq_len, num_key_value_heads, head_dim), indices_k)
- value_layer = _index_first_axis(
- value_layer.reshape(batch_size * kv_seq_len, num_key_value_heads, head_dim), indices_k
- )
- if query_length == kv_seq_len:
- query_layer = _index_first_axis(query_layer.reshape(batch_size * kv_seq_len, -1, head_dim), indices_k)
- cu_seqlens_q = cu_seqlens_k
- max_seqlen_in_batch_q = max_seqlen_in_batch_k
- indices_q = indices_k
- elif query_length == 1:
- max_seqlen_in_batch_q = 1
- cu_seqlens_q = torch.arange(
- batch_size + 1, dtype=torch.int32, device=query_layer.device
- ) # There is a memcpy here, that is very bad.
- indices_q = cu_seqlens_q[:-1]
- query_layer = query_layer.squeeze(1)
- else:
- raise NotImplementedError()
-
- return (
- query_layer,
- key_layer,
- value_layer,
- indices_q,
- (cu_seqlens_q, cu_seqlens_k),
- (max_seqlen_in_batch_q, max_seqlen_in_batch_k),
- )
-
-def flash_attn_prefill(
- module: torch.nn.Module,
- query_states: torch.Tensor,
- key_states: torch.Tensor,
- value_states: torch.Tensor,
- attention_mask: torch.Tensor,
- dropout: float,
- scaling: float,
- query_length: int,
- batch_size: int,
- indices_k: torch.Tensor,
- cu_seqlens_k: torch.Tensor,
- max_seqlen_in_batch_k: int,
- **kwargs
-):
- """
- Wrapper for flash attention during the prefill stage
- query_states must have shape (batch_size, num_heads, seq_len, head_dim)
- key_states and value_states must have shape (batch_size, num_kv_heads, kv_len, head_dim)
-
- This is the opposite of what is required by flash attention, but keeps parity with the HF convention
-
- query_length, batch_size, indices_k, cu_seqlens_k, and max_seqlen_in_batch_k should come from the flash attention kwargs
- """
- query_states, key_states, value_states = query_states.transpose(1,2), key_states.transpose(1,2), value_states.transpose(1,2)
- q_flash, k_flash, v_flash, indices_q, cu_seq_lens, max_seq_lens = _upad_input(
- query_states, key_states, value_states, query_length, indices_k, cu_seqlens_k, max_seqlen_in_batch_k
- )
- cu_seqlens_q, cu_seqlens_k = cu_seq_lens
- max_seqlen_in_batch_q, max_seqlen_in_batch_k = max_seq_lens
-
- # Returning None for attn_weights to match other attention interfaces
- flash_attn_out = _flash_attn_varlen_func(
- q_flash,
- k_flash,
- v_flash,
- cu_seqlens_q=cu_seqlens_q,
- cu_seqlens_k=cu_seqlens_k,
- max_seqlen_q=max_seqlen_in_batch_q,
- max_seqlen_k=max_seqlen_in_batch_k,
- dropout_p=dropout,
- softmax_scale=scaling,
- causal=module.is_causal,
- )
- return pad_input(flash_attn_out, indices_q, batch_size, query_length), None
-
-# NOTE: Does not support dropout, accepts argument as kwargs to maintain compatibility
-# This function is an order of magnitude faster than the prefill variant, or using the HF interface
-def flash_attn_decode(
- module: torch.nn.Module,
- query_states: torch.Tensor,
- key_states: torch.Tensor,
- value_states: torch.Tensor,
- attention_mask: torch.Tensor,
- scaling: float,
- **kwargs,
-):
- """
- Wrapper for flash attention during the decode stage
-
- query_states must have shape (batch_size, num_heads, seq_len, head_dim), 1 is the seq length in the decoding stage
- key_states and value_states must have shape (batch_size, num_kv_heads, kv_len, head_dim)
-
- This is the opposite of what is required by flash attention, but keeps parity with the HF convention
-
- This function computes the left pad and cache seqlens to pass into FA2. For example -
- Given an attention_mask shaped (batch_size=2, seq_len=8), where 0 = padding, 1 = real token
- attention_mask =
- tensor([
- [0, 0, 1, 1, 1, 0, 0, 0], # ← batch 0
- [0, 1, 1, 1, 1, 1, 1, 0], # ← batch 1
- ])
- cache_leftpad = tensor([2, 1], dtype=torch.int32)
- cache_seqlens = tensor([5, 7], dtype=torch.int32)
- These values allow FlashAttention to use a static cache layout with efficient slicing during decoding.
- """
- query_states, key_states, value_states = query_states.transpose(1,2), key_states.transpose(1,2), value_states.transpose(1,2)
-
- cache_leftpad = (attention_mask == 0).cumprod(dim=1).sum(dim=1).to(torch.int32)
- cache_seqlens = (attention_mask * torch.arange(attention_mask.size(1), device=attention_mask.device)).argmax(dim=1).to(torch.int32) + 1
-
- # Returning None for attn_weights to match other attention interfaces
- return _flash_attn_with_kvcache(
- q=query_states,
- k_cache=key_states,
- v_cache=value_states,
- cache_leftpad=cache_leftpad,
- cache_seqlens=cache_seqlens,
- causal=module.is_causal,
- softmax_scale=scaling,
- ), None
\ No newline at end of file
diff --git a/surya/common/surya/processor/__init__.py b/surya/common/surya/processor/__init__.py
deleted file mode 100644
index 99989b2a..00000000
--- a/surya/common/surya/processor/__init__.py
+++ /dev/null
@@ -1,481 +0,0 @@
-import math
-
-import cv2
-import numpy as np
-import torch
-from PIL import Image
-from torch.nn.utils.rnn import pad_sequence
-
-from typing import List, Optional, Tuple
-
-from transformers.feature_extraction_utils import BatchFeature
-from transformers.processing_utils import ProcessorMixin
-from transformers.tokenization_utils import PreTrainedTokenizer
-
-from surya.common.s3 import S3DownloaderMixin
-from surya.common.surya.processor.schema import (
- TextInput,
- ImageInput,
- ProcessorOutput,
-)
-from surya.common.surya.schema import TaskNames
-from surya.logging import get_logger
-from surya.settings import settings
-
-logger = get_logger()
-
-# Task agnostic tokens - Every task will use these in some form or another
-EOS_TOKEN = ""
-EOI_TOKEN = "" # This is end of INPUT, not image. Images are always followed by a task specific BOS token, so that serves as a delimiter anyways.
-IMAGE_TOKEN = ""
-PAD_TOKEN = ""
-NO_OUTPUT_TOKEN = ""
-IMAGE_ROTATED_TOKEN = ""
-REGISTER_TOKENS = ["", "", "", ""]
-BEACON_TOKEN = ""
-NOMATH_TOKEN = ""
-
-# Task specific tokens
-OCR_WITH_BOXES_BOS_TOKEN = ""
-OCR_WITHOUT_BOXES_BOS_TOKEN = ""
-BLOCK_WITHOUT_BOXES_TOKEN = ""
-LAYOUT_BOS_TOKEN = ""
-TABLE_STRUCTURE_BOS_TOKEN = ""
-
-
-class SuryaOCRProcessor(S3DownloaderMixin, ProcessorMixin):
- attributes = ["image_processor", "ocr_tokenizer"]
- image_processor_class = "BaseImageProcessor"
- ocr_tokenizer_class = "PreTrainedTokenizer"
- rescale_factor = 1 / 255.0
- image_mean = (0.485, 0.456, 0.406)
- image_std = (0.229, 0.224, 0.225)
-
- def __init__(
- self,
- ocr_tokenizer: PreTrainedTokenizer,
- blank_bbox_token_id: int,
- num_register_tokens: int,
- patch_size: int,
- merge_size: int,
- num_beacon_tokens: int,
- beacon_token_interval: int,
- model_device: str,
- **kwargs,
- ):
- self.ocr_tokenizer = ocr_tokenizer
- self.patch_size = patch_size
- self.merge_size = merge_size
- self.num_register_tokens = num_register_tokens
- self.num_beacon_tokens = num_beacon_tokens
- self.beacon_token_interval = beacon_token_interval
-
- self.tokenizer_vocab_size = 0
- for attr in self.attributes:
- if "tokenizer" in attr:
- self.tokenizer_vocab_size += getattr(self, attr).vocab_size
-
- self.offsets = {"ocr": 0}
-
- # Create special token mapping
- self.special_token_mapping = self.ocr_tokenizer.system_tokens
-
- self.register_token_ids = [
- self.special_token_mapping.get(r) for r in REGISTER_TOKENS
- ]
- self.beacon_token_id = self.special_token_mapping.get(BEACON_TOKEN)
- self.image_token_id = self.special_token_mapping.get(IMAGE_TOKEN)
- self.pad_token_id = self.special_token_mapping.get(PAD_TOKEN)
- self.eos_token_id = self.special_token_mapping.get(EOS_TOKEN)
- self.eoi_token_id = self.special_token_mapping.get(EOI_TOKEN)
- self.no_output_token = self.special_token_mapping.get(NO_OUTPUT_TOKEN)
- self.image_rotated_token = self.special_token_mapping.get(IMAGE_ROTATED_TOKEN)
- self.nomath_token = self.special_token_mapping.get(NOMATH_TOKEN)
-
- self.bos_token_id = {
- TaskNames.ocr_with_boxes: self.special_token_mapping.get(
- OCR_WITH_BOXES_BOS_TOKEN
- ),
- TaskNames.ocr_without_boxes: self.special_token_mapping.get(
- OCR_WITHOUT_BOXES_BOS_TOKEN
- ),
- TaskNames.block_without_boxes: self.special_token_mapping.get(
- BLOCK_WITHOUT_BOXES_TOKEN
- ),
- TaskNames.layout: self.special_token_mapping.get(LAYOUT_BOS_TOKEN),
- TaskNames.table_structure: self.special_token_mapping.get(
- TABLE_STRUCTURE_BOS_TOKEN
- ),
- }
-
- if self.image_token_id is None:
- logger.warning("Warning: Image token not found in special tokens")
-
- self.blank_bbox_token_id = blank_bbox_token_id
- self.bbox_pad_token_id = self.blank_bbox_token_id
-
- self.ignore_bbox_token_ids = [
- v
- for (k, v) in self.ocr_tokenizer.SPECIAL_TOKEN_MAPPING.items()
- if k not in self.ocr_tokenizer.special_tokens["math_external"]
- ]
- math_end_token = ""
- self.math_start_token_ids = [
- v
- for (k, v) in self.ocr_tokenizer.SPECIAL_TOKEN_MAPPING.items()
- if k in self.ocr_tokenizer.special_tokens["math_external"]
- and k != math_end_token
- ]
- self.math_end_token_ids = [
- v
- for (k, v) in self.ocr_tokenizer.SPECIAL_TOKEN_MAPPING.items()
- if k == math_end_token
- ]
-
- if self.num_register_tokens > len(self.register_token_ids):
- raise ValueError(
- "The number of register tokens requested exceeds the number of register tokens defined in the special token mapping."
- )
-
- self.image_mean = np.array(self.image_mean, dtype=np.float32)
- self.image_std = np.array(self.image_std, dtype=np.float32)
- self.model_device = model_device
-
- @property
- def vocab_size(self):
- return self.tokenizer_vocab_size
-
- def image_processor(self, image: Image.Image) -> np.ndarray:
- # Convert to array
- image = np.asarray(image, dtype=np.float32)
- return image
-
- @staticmethod
- def scale_to_fit(
- img: np.ndarray,
- max_size: Tuple[int, int],
- min_size: Tuple[int, int] = (168, 168),
- ):
- # Get current dimensions
- height, width = img.shape[:2]
-
- # Check for empty or invalid image
- if width == 0 or height == 0:
- return img
-
- max_width, max_height = max_size
- min_width, min_height = min_size
-
- # Calculate pixel counts
- current_pixels = width * height
- max_pixels = max_width * max_height
- min_pixels = min_width * min_height
-
- if current_pixels > max_pixels:
- scale_factor = (max_pixels / current_pixels) ** 0.5
-
- new_width = math.floor(width * scale_factor)
- new_height = math.floor(height * scale_factor)
- elif current_pixels == 0:
- return img
- elif current_pixels < min_pixels:
- scale_factor = (min_pixels / current_pixels) ** 0.5
-
- new_width = math.ceil(width * scale_factor)
- new_height = math.ceil(height * scale_factor)
- else:
- return img
-
- return cv2.resize(
- img, (new_width, new_height), interpolation=cv2.INTER_LANCZOS4
- )
-
- def _image_processor(self, image: np.ndarray):
- image = image.astype(np.float64) * self.rescale_factor
- image = (image.astype(np.float32) - self.image_mean) / self.image_std
- return image
-
- def _process_and_tile(
- self, image: np.ndarray
- ) -> Tuple[torch.Tensor, Tuple[int, int, int]]:
- """
- Resizes the input image to the closest multiple of tile_size while preserving the aspect ratio
- and returns a tensor of image tiles.
- """
- extra_multipler = (
- 4 if settings.FOUNDATION_XLA else 1
- ) # Needed to force same size grid_thws per row with padding
-
- factor = (
- self.patch_size * self.merge_size * extra_multipler
- ) # Make a multiple of window size
-
- height, width = image.shape[:2]
-
- h_bar = math.ceil(height / factor) * factor
- w_bar = math.ceil(width / factor) * factor
- if h_bar != height or w_bar != width:
- if height == 0 or width == 0:
- image = np.zeros((h_bar, w_bar, 3), dtype=np.uint8)
- else:
- image = cv2.resize(image, (w_bar, h_bar), interpolation=cv2.INTER_CUBIC)
-
- # Handle scaling and normalization
- image = self._image_processor(image)
- height, width = image.shape[:2]
-
- # Numpy array to torch tensor
- img_tensor = torch.from_numpy(image.transpose(2, 0, 1))
- patches = img_tensor.unsqueeze(0)
-
- channel = patches.shape[1]
- grid_t = patches.shape[0]
- grid_h, grid_w = height // self.patch_size, width // self.patch_size
-
- patches = patches.reshape(
- grid_t,
- 1,
- channel,
- grid_h // self.merge_size,
- self.merge_size,
- self.patch_size,
- grid_w // self.merge_size,
- self.merge_size,
- self.patch_size,
- )
- patches = patches.permute(0, 3, 6, 4, 7, 2, 1, 5, 8)
- flatten_patches = patches.reshape(
- grid_t * grid_h * grid_w, channel * 1 * self.patch_size * self.patch_size
- )
-
- return flatten_patches, (grid_t, grid_h, grid_w)
-
- # Handle image input dictionaries - Process image, tile accordingly, and setup the input ids and boxes correspondingly
- def _process_image_input(self, image_input: ImageInput) -> ProcessorOutput:
- rotated = image_input.get("rotated", False)
- image = image_input.get("image", None)
-
- assert image is not None, (
- "A PIL Image must be provided when the input type is `image`"
- )
- image_tiles, grid_thw = self._process_and_tile(image)
-
- num_tokens = image_tiles.shape[0] / self.merge_size**2
- assert num_tokens.is_integer(), (
- f"Expected number of tokens to be an integer, got {num_tokens}"
- )
-
- input_ids = [self.image_token_id] * int(num_tokens)
- input_ids += self.register_token_ids[: self.num_register_tokens]
-
- # Handle the image being rotated in the imdataset
- if rotated:
- input_ids = [self.image_rotated_token] + input_ids
-
- return ProcessorOutput(
- input_ids=input_ids,
- image_tiles=image_tiles,
- grid_thw=grid_thw,
- )
-
- def _process_text_input(self, text_input: TextInput, task: str) -> ProcessorOutput:
- input_text = text_input.get("text", None)
- math_mode = text_input.get("math", False)
-
- input_ids = self.ocr_tokenizer(input_text, tasks=task)["input_ids"][0]
- input_ids = [self.offsets["ocr"] + id for id in input_ids]
-
- # nomath token does not work for layout
- if not math_mode and task != "layout":
- input_ids.insert(0, self.nomath_token)
-
- return ProcessorOutput(
- input_ids=input_ids,
- image_tiles=None,
- grid_thw=None,
- )
-
- def _process_input(self, input_dict: dict, task: str):
- input_type = input_dict["type"]
- if input_type == "image":
- return self._process_image_input(input_dict)
- elif input_type == "text":
- return self._process_text_input(input_dict, task)
-
- raise NotImplementedError(f"Input of type `{input_type}` is not implemented")
-
- # Peprocessing for OCR task
- # The task is expected to have - image_dict, user_input_dict, output_dict
- # use_input_dict is allowed to have an empty input which is fine, but needs to be present
- def _process_ocr_with_boxes(
- self,
- mixed_input: List[dict],
- bos_token_id: int,
- task: str = TaskNames.ocr_with_boxes,
- ):
- processed_input_ids = []
- all_image_tiles = []
- all_grid_thw = []
-
- # 1. Process the image input
- for i, input_dict in enumerate(mixed_input):
- processor_output = self._process_input(input_dict, task)
- input_ids = processor_output["input_ids"]
- image_tiles = processor_output["image_tiles"]
- grid_thw = processor_output["grid_thw"]
-
- # Special handling of some delimiter tokens
- if i == 1:
- assert input_dict["type"] == "text", (
- "Expected text input for model input."
- )
- # Case for input - Add task specific bos token + end_of_input token
- # We do not want the model to learn how to predict inputs. Hence IGNORE_INDEX for these
- input_ids = [bos_token_id] + input_ids + [self.eoi_token_id]
- if i == 2:
- assert input_dict["type"] == "text", (
- "Expected text for final model input"
- )
- input_ids = input_ids + [self.eos_token_id]
- elif i > 2:
- raise ValueError(f"Too many inputs received. Expected is 2 for inference, 3 for training. Received: {len(mixed_input)}")
-
- # Some input types don't return any image tiles, accounting for that
- if image_tiles is not None:
- all_image_tiles.append(image_tiles)
- all_grid_thw.append(grid_thw)
-
- processed_input_ids.extend(input_ids)
-
- return (
- torch.tensor(processed_input_ids, dtype=torch.long),
- all_image_tiles,
- all_grid_thw,
- )
-
- def _process_layout(self, mixed_input: List[dict], bos_token_id: int):
- return self._process_ocr_with_boxes(
- mixed_input, bos_token_id=bos_token_id, task="layout"
- )
-
- def _process_table_structure(self, mixed_input: List[dict], bos_token_id: int):
- return self._process_ocr_with_boxes(
- mixed_input, bos_token_id=bos_token_id, task="table_structure"
- )
-
- def _process_ocr_without_boxes(
- self,
- mixed_input: List[dict],
- bos_token_id: int,
- task: str = "ocr_without_boxes",
- ):
- # Boxes are set to None, so this will work
- # TODO: improve this behavior
- return self._process_ocr_with_boxes(
- mixed_input, bos_token_id=bos_token_id, task=task
- )
-
- def _process_block_without_boxes(
- self,
- mixed_input: List[dict],
- bos_token_id: int,
- task: str = "block_without_boxes",
- ):
- return self._process_ocr_with_boxes(
- mixed_input, bos_token_id=bos_token_id, task=task
- )
-
- def align_long_axis(self, image: np.ndarray) -> Tuple[np.ndarray, bool]:
- height, width, _ = image.shape
- if height > width: # Rotate vertical lines
- image = cv2.rotate(image, cv2.ROTATE_90_COUNTERCLOCKWISE)
- return image, True
-
- return image, False
-
- def __call__(
- self,
- mixed_batch: List[dict],
- padding_side: Optional[str] = "left",
- device: Optional[torch.device] = None,
- pad_to_multiple: Optional[int] = None,
- ):
- all_image_tiles = []
- all_input_ids = []
- all_grid_thw = []
-
- for b in mixed_batch:
- mixed_input = b["inputs"]
- task = b["task"]
- assert task in self.bos_token_id, f"Task {task} has no bos token defined."
-
- # Select the correct processing function based on the task type
- input_ids, image_tiles, grid_thw = getattr(self, f"_process_{task}")(
- mixed_input, self.bos_token_id[task]
- )
-
- all_input_ids.append(input_ids)
- all_image_tiles.extend(image_tiles)
- all_grid_thw.extend(grid_thw)
-
- batched_input_ids = pad_sequence(
- all_input_ids,
- batch_first=True,
- padding_side=padding_side,
- padding_value=self.pad_token_id,
- )
-
- if pad_to_multiple is not None:
- current_len = batched_input_ids.shape[1]
- # Calculate the next multiple of pad_to_multiple
- padded_len = (
- (current_len + pad_to_multiple - 1) // pad_to_multiple
- ) * pad_to_multiple
-
- if padded_len > current_len:
- pad_len = padded_len - current_len
- batched_input_ids = torch.nn.functional.pad(
- batched_input_ids, (pad_len, 0), value=self.pad_token_id
- )
-
- attention_mask = batched_input_ids.ne(self.pad_token_id)
-
- # Generating position IDs that are independent of left and right padding;
- # This should ensure same results for either padding side. Exact position id for the pad tokens themselves don't matter since they are masked
- position_ids = attention_mask.cumsum(dim=-1) - 1
- position_ids[position_ids < 0] = (
- 0 # For left padding, the position ids for padding will become -1 because of the shift; Setting to 0
- )
- position_ids = (
- attention_mask.to(torch.long) * position_ids
- ) # Ensure right pad ids get set to zero
-
- batched_image_tiles = torch.cat(all_image_tiles, dim=0)
- batched_grid_thw = torch.from_numpy(np.array(all_grid_thw))
-
- # Pin memory for CUDA
- if device == torch.device("cuda"):
- batched_image_tiles = batched_image_tiles.pin_memory()
- batched_grid_thw = batched_grid_thw.pin_memory()
- attention_mask = attention_mask.pin_memory()
- batched_input_ids = batched_input_ids.pin_memory()
- position_ids = position_ids.pin_memory()
-
- return BatchFeature(
- {
- "input_ids": batched_input_ids,
- "image_tiles": batched_image_tiles,
- "attention_mask": attention_mask,
- "position_ids": position_ids,
- "grid_thw": batched_grid_thw,
- }
- )
-
- # Decode model outputs; Strips special tokens
- def decode(self, tokens: List[int], task: str):
- filtered_tokens = [
- t
- for t in tokens
- if t not in self.special_token_mapping.values() and t != -100
- ] # Skip special tokens and loss ignore index
- return self.ocr_tokenizer.decode(filtered_tokens, task=task)
diff --git a/surya/common/surya/processor/schema.py b/surya/common/surya/processor/schema.py
deleted file mode 100644
index 5f570577..00000000
--- a/surya/common/surya/processor/schema.py
+++ /dev/null
@@ -1,37 +0,0 @@
-from typing import TypedDict, Literal, List, Tuple
-
-import torch
-from PIL import Image
-
-
-class TaskDict(TypedDict):
- datasets: List[str]
- img_size: Tuple[int, int]
-
-
-class TasksDict(TypedDict):
- ocr_with_boxes: TaskDict
- ocr_without_boxes: TaskDict
- block_without_boxes: TaskDict
-
-
-class ProcessorInput(TypedDict):
- type: Literal["image", "ocr", "text", "empty_output"]
-
-
-class ImageInput(ProcessorInput):
- type: Literal["image"]
- image: Image.Image
- rotated: bool
-
-
-class TextInput(ProcessorInput):
- type: Literal["text"]
- text: str
- math: bool
-
-
-class ProcessorOutput(TypedDict):
- input_ids: List[int]
- image_tiles: torch.Tensor | None
- grid_thw: torch.Tensor | None
diff --git a/surya/common/surya/processor/tokenizer.py b/surya/common/surya/processor/tokenizer.py
deleted file mode 100644
index 9b98fa8c..00000000
--- a/surya/common/surya/processor/tokenizer.py
+++ /dev/null
@@ -1,594 +0,0 @@
-import html
-import re
-from typing import List, Union, Dict, Optional, Tuple, Iterable
-import numpy as np
-import torch
-from tokenizers import AddedToken
-import json
-import os
-from transformers import PreTrainedTokenizer, Qwen2Tokenizer as Qwen2OriginalTokenizer
-
-
-from surya.common.s3 import S3DownloaderMixin
-from surya.common.surya.schema import TASK_NAMES, TaskNames
-from surya.logging import get_logger
-from surya.settings import settings
-
-logger = get_logger()
-
-
-def create_token_regex(tokens):
- escaped_tokens = [re.escape(token) for token in tokens]
- escaped_tokens.sort(key=len, reverse=True)
- pattern = r"^(" + "|".join(escaped_tokens) + r")"
- regex = re.compile(pattern)
- return regex
-
-
-class Qwen2Tokenizer(S3DownloaderMixin, Qwen2OriginalTokenizer):
- pass
-
-class GreedyMathUTF16Tokenizer(S3DownloaderMixin, PreTrainedTokenizer):
- """
- HuggingFace slow tokenizer implementing:
- - UTF-16 code units as the base [0..65535]
- - Math tokens as greedy-longest-match ids after UTF-16
- - Literal special tokens after math tokens
- Absolute ID layout:
- [0 .. 65535] : UTF-16 units
- [65536 .. 65536+M-1] : math tokens
- [65536+M .. 65536+M+S-1] : special tokens
- """
-
- vocab_files_names = {
- "vocab_file": "vocab_math.json", # {"\\frac": 0, "\\alpha": 1, ...} raw contiguous ids 0..M-1
- "specials_file": "specials.json", # [flat list for legacy]
- "specials_dict_file": "specials_dict.json", # category dict (preferred)
- }
- model_input_names = ["input_ids", "attention_mask"]
- is_fast = False
-
- # ---------- helpers ----------
- @staticmethod
- def _to_utf16_units(s: str) -> List[int]:
- b = s.encode("utf-16le")
- return [int.from_bytes(b[i : i + 2], "little") for i in range(0, len(b), 2)]
-
- @staticmethod
- def _from_utf16_units(units: List[int]) -> str:
- b = bytearray()
- for u in units:
- b += int(u).to_bytes(2, "little")
- return b.decode("utf-16le", errors="ignore")
-
- class _TrieNode:
- __slots__ = ("child", "id", "leaf")
-
- def __init__(self):
- self.child: Dict[str, "GreedyMathUTF16Tokenizer._TrieNode"] = {}
- self.id: Optional[int] = None
- self.leaf: bool = False
-
- @classmethod
- def _build_trie(
- cls, token_to_id: Dict[str, int]
- ) -> "GreedyMathUTF16Tokenizer._TrieNode":
- root = cls._TrieNode()
- for tok, tid in token_to_id.items():
- node = root
- for ch in tok:
- node = node.child.setdefault(ch, cls._TrieNode())
- node.leaf = True
- node.id = tid
- return root
-
- def _build_escape_patterns(self, math_token_to_rawid):
- """Build pattern list from vocab commands that start with control characters.
-
- Scans the math vocab for LaTeX commands that could be corrupted by JSON
- escape sequence interpretation (e.g., \\begin becomes egin).
- """
- control_chars = {
- '\x08': 'b', # backspace
- '\t': 't', # tab
- '\n': 'n', # newline
- '\r': 'r', # carriage return
- '\f': 'f', # form feed
- '\x07': 'a', # bell
- '\x0b': 'v', # vertical tab
- }
-
- patterns = {char: [] for char in control_chars}
-
- for token in math_token_to_rawid.keys():
- if token.startswith('\\') and len(token) > 1:
- letter = token[1:2] # First char after backslash
- for ctrl_char, ctrl_letter in control_chars.items():
- if letter == ctrl_letter:
- # This token could be corrupted: \token -> oken
- suffix = token[2:] # Everything after \X
- patterns[ctrl_char].append((suffix, token))
-
- # Sort by length (longest first) to avoid partial matches
- for char in patterns:
- patterns[char].sort(key=lambda x: len(x[0]), reverse=True)
-
- return patterns
-
- @classmethod
- def _encode_math_greedy(
- cls,
- s: str,
- trie: "GreedyMathUTF16Tokenizer._TrieNode",
- math_base: int,
- debug: bool = False,
- ) -> List[int]:
- i, n = 0, len(s)
- out: List[int] = []
- while i < n:
- node = trie
- j = i
- last_id = None
- last_j = i
- while j < n and (ch := s[j]) in node.child:
- node = node.child[ch]
- j += 1
- if node.leaf:
- last_id, last_j = node.id, j
- if last_id is not None:
- if debug:
- print(f"[MATH] matched {s[i:last_j]!r} -> {last_id}")
- out.append(math_base + last_id)
- i = last_j
- else:
- units = cls._to_utf16_units(s[i])
- if debug:
- print(f"[MATH] fallback {s[i]!r} -> utf16 {units}")
- out.extend(units)
- i += 1
- return out
-
- # ---------- init ----------
- def __init__(
- self,
- vocab_file: Optional[str] = None,
- specials_file: Optional[str] = None,
- specials_dict_file: Optional[str] = None,
- *,
- # You can also pass programmatically instead of files:
- math_vocab: Optional[Dict[str, int]] = None,
- special_tokens: Optional[List[str]] = None,
- special_tokens_dict: Optional[Dict[str, List[str]]] = None,
- debug: bool = False,
- # Standard HF special token kwargs:
- bos_token: Optional[str] = None,
- eos_token: Optional[str] = None,
- pad_token: Optional[str] = None,
- unk_token: Optional[str] = None,
- **kwargs,
- ):
- # Load math vocab
- if vocab_file and os.path.isfile(vocab_file):
- with open(vocab_file, "r", encoding="utf-8") as f:
- mv = json.load(f)
- else:
- mv = math_vocab or {}
-
- # Make math ids contiguous if needed
- if mv:
- max_id = max(mv.values())
- if set(mv.values()) != set(range(max_id + 1)):
- items = sorted(mv.items(), key=lambda kv: kv[1])
- mv = {tok: i for i, (tok, _) in enumerate(items)}
-
- # Load special tokens (prefer category dict; fallback to flat list or defaults)
- sp_dict = None
- if specials_dict_file and os.path.isfile(specials_dict_file):
- with open(specials_dict_file, "r", encoding="utf-8") as f:
- sp_dict = json.load(f)
- elif special_tokens_dict is not None:
- sp_dict = dict(special_tokens_dict)
-
- if sp_dict is None:
- # Legacy path: flat list from file or provided/default list
- if specials_file and os.path.isfile(specials_file):
- with open(specials_file, "r", encoding="utf-8") as f:
- sp_list_flat = json.load(f)
- else:
- sp_list_flat = special_tokens or SPECIAL_TOKENS
- sp_dict = {"all": list(sp_list_flat)}
-
- # Ensure "all" exists and is unique/preserved in order.
- if "all" not in sp_dict or not isinstance(sp_dict["all"], list):
- order = [
- "system",
- "formatting",
- "math_external",
- "script",
- "layout",
- "reasoning",
- "table_structure",
- "reserved",
- ]
- seen = set()
- all_tokens: List[str] = []
- for k in order:
- if k in sp_dict and isinstance(sp_dict[k], list):
- for t in sp_dict[k]:
- if t not in seen:
- all_tokens.append(t)
- seen.add(t)
- sp_dict["all"] = all_tokens
-
- # Keep a copy of categories (if present) for downstream processor logic.
- self.special_tokens = sp_dict
- sp_list = list(sp_dict.get("all", []))
- # Regex list should favor longest-first to avoid partial matches.
- specials_for_regex = sorted(sp_list, key=len, reverse=True)
-
- self.debug = debug
- self.UTF16_SPACE = 65536
- self.math_token_to_rawid = dict(mv) # 0..M-1
- self.math_vocab_size = len(self.math_token_to_rawid)
- self.MATH_BASE = self.UTF16_SPACE
- self.SPECIAL_BASE = self.UTF16_SPACE + self.math_vocab_size
-
- # Maps
- self.math_absid_to_token = {
- self.MATH_BASE + rid: tok for tok, rid in self.math_token_to_rawid.items()
- }
- self.special_tokens_list = sp_list # ID assignment order
- self.special_to_absid = {
- tok: self.SPECIAL_BASE + i for i, tok in enumerate(self.special_tokens_list)
- }
- self.absid_to_special = {v: k for k, v in self.special_to_absid.items()}
-
- # Public attributes for legacy/processor:
- # All specials mapping (token -> absolute id)
- self.SPECIAL_TOKEN_MAPPING: Dict[str, int] = dict(self.special_to_absid)
- # Subset used heavily by processor for quick access
- self.reverse_special_token_mapping = {
- v: k for k, v in self.SPECIAL_TOKEN_MAPPING.items()
- }
- self.LAYOUT_LABEL2ID = {
- k: v
- for k, v in self.SPECIAL_TOKEN_MAPPING.items()
- if k in self.special_tokens["layout"]
- }
- self.TABLE_STRUCTURE_LABEL2ID = {
- k: v
- for k, v in self.SPECIAL_TOKEN_MAPPING.items()
- if k in self.special_tokens["table_structure"]
- }
- if not self.special_tokens.get("system", []):
- print("Warning: No system tokens found in special_tokens")
-
- self.MATH_TAG_START = "")
- kwargs.setdefault("pad_token", pad_token or "")
- kwargs.setdefault("unk_token", unk_token)
-
- super().__init__(
- vocab_file=vocab_file,
- specials_file=specials_file,
- specials_dict_file=specials_dict_file,
- **kwargs,
- )
-
- # ---------- required HF surface ----------
- @property
- def vocab_size(self) -> int:
- return self.UTF16_SPACE + self.math_vocab_size + len(self.special_tokens_list)
-
- def get_vocab(self) -> Dict[str, int]:
- # Compact vocab: just math+specials with ABSOLUTE ids.
- v = {tok: self.MATH_BASE + rid for tok, rid in self.math_token_to_rawid.items()}
- v.update(self.special_to_absid)
- return v
-
- def __len__(self) -> int:
- return self.vocab_size
-
- # Core encode/decode on ABSOLUTE ids
- def _encode_core(self, text: str) -> List[int]:
- text = html.unescape(text)
- ids: List[int] = []
- in_math = False
- chunks = self.specials_pattern.split(text) if self.specials_pattern else [text]
- for chunk in chunks:
- if chunk in self.special_to_absid:
- ids.append(self.special_to_absid[chunk])
- if chunk.startswith(""):
- in_math = False
- if self.debug:
- print(f"[TAG] {chunk!r} -> {self.special_to_absid[chunk]}")
- continue
-
- if in_math:
- ids.extend(
- self._encode_math_greedy(
- chunk, self.trie, self.MATH_BASE, debug=self.debug
- )
- )
- else:
- units = self._to_utf16_units(chunk)
- if self.debug and units:
- print(
- f"[TEXT] utf16 {chunk[:32]!r} -> {units[:8]}{'...' if len(units) > 8 else ''}"
- )
- ids.extend(units)
- return ids
-
- def _fix_latex_escapes(self, text: str) -> str:
- """Fix improperly escaped LaTeX commands in decoded text.
-
- Operates on the complete decoded string, replacing control character
- sequences with their intended LaTeX commands based on vocab patterns.
- """
- result = []
- i = 0
- while i < len(text):
- char = text[i]
- if char in self.latex_escape_patterns:
- # Check if any pattern matches
- matched = False
- for pattern, replacement in self.latex_escape_patterns[char]:
- if text[i+1:].startswith(pattern):
- result.append(replacement)
- i += 1 + len(pattern)
- matched = True
- break
- if not matched:
- # Not a LaTeX command, keep the control char as-is
- result.append(char)
- i += 1
- else:
- result.append(char)
- i += 1
-
- return ''.join(result)
-
- def _decode_core(self, ids: Iterable[int]) -> str:
- out: List[str] = []
- buf: List[int] = []
-
- def flush():
- if buf:
- out.append(self._from_utf16_units(buf))
- buf.clear()
-
- for tid in ids:
- if tid >= self.MATH_BASE and tid < self.SPECIAL_BASE:
- flush()
- out.append(self.math_absid_to_token.get(tid, ""))
- elif tid >= self.SPECIAL_BASE:
- flush()
- out.append(self.absid_to_special.get(tid, ""))
- else:
- buf.append(int(tid))
- flush()
- decoded = "".join(out)
- return self._fix_latex_escapes(decoded)
-
- # ---- Tokenizer interface ----
- def _tokenize(self, text: str, **kwargs) -> List[str]:
- ids = self._encode_core(text)
- toks: List[str] = []
- for i in ids:
- if i < self.MATH_BASE:
- toks.append(f"")
- elif i < self.SPECIAL_BASE:
- toks.append(self.math_absid_to_token.get(i, ""))
- else:
- toks.append(self.absid_to_special.get(i, ""))
- return toks
-
- def _convert_token_to_id(self, token: str) -> int:
- if token.startswith(""):
- try:
- return int(token[3:-1], 16) # UTF-16 unit
- except Exception:
- return self.unk_token_id if self.unk_token_id is not None else 0
- # math or specials
- if token in self.math_token_to_rawid:
- return self.MATH_BASE + self.math_token_to_rawid[token]
- if token in self.special_to_absid:
- return self.special_to_absid[token]
- # rare path: single-char token -> its UTF-16 unit
- if len(token) == 1:
- u = self._to_utf16_units(token)
- if len(u) == 1:
- return u[0]
- return self.unk_token_id if self.unk_token_id is not None else 0
-
- def _convert_id_to_token(self, index: int) -> str:
- if index < self.MATH_BASE:
- return f""
- if index < self.SPECIAL_BASE:
- return self.math_absid_to_token.get(index, "")
- return self.absid_to_special.get(index, "")
-
- def convert_tokens_to_string(self, tokens: List[str]) -> str:
- ids = [self._convert_token_to_id(t) for t in tokens]
- return self._decode_core(ids)
-
- def decode(self, token_ids, skip_special_tokens: bool = False, **kwargs) -> str:
- # Accept int, list, tuple, numpy, torch
- if hasattr(token_ids, "tolist"):
- token_ids = token_ids.tolist()
- elif isinstance(token_ids, int):
- token_ids = [token_ids]
- else:
- token_ids = list(token_ids)
- token_ids = [int(i) for i in token_ids] # normalize early
-
- if skip_special_tokens:
- token_ids = [i for i in token_ids if i < self.SPECIAL_BASE]
- return self._decode_core(token_ids)
-
- # HF plumbing
- def build_inputs_with_special_tokens(
- self, token_ids_0: List[int], token_ids_1: Optional[List[int]] = None
- ) -> List[int]:
- out = (
- list(token_ids_0)
- if token_ids_1 is None
- else list(token_ids_0) + list(token_ids_1)
- )
- # if self.eos_token_id is not None and (not out or out[-1] != self.eos_token_id):
- # out.append(self.eos_token_id)
- return out
-
- def get_special_tokens_mask(
- self,
- token_ids_0: List[int],
- token_ids_1: Optional[List[int]] = None,
- already_has_special_tokens: bool = False,
- ) -> List[int]:
- def mask(seq: List[int]) -> List[int]:
- return [1 if i >= self.SPECIAL_BASE else 0 for i in seq]
-
- return (
- mask(token_ids_0)
- if token_ids_1 is None
- else mask(token_ids_0) + mask(token_ids_1)
- )
-
- def create_token_type_ids_from_sequences(
- self, token_ids_0: List[int], token_ids_1: Optional[List[int]] = None
- ) -> List[int]:
- return [0] * (
- len(token_ids_0)
- if token_ids_1 is None
- else len(token_ids_0) + len(token_ids_1)
- )
-
- # Save/load raw assets
- def save_vocabulary(
- self, save_directory: str, filename_prefix: Optional[str] = None
- ) -> Tuple[str, str]:
- os.makedirs(save_directory, exist_ok=True)
- pre = (filename_prefix + "-") if filename_prefix else ""
- vocab_path = os.path.join(
- save_directory, pre + self.vocab_files_names["vocab_file"]
- )
- specials_path = os.path.join(
- save_directory, pre + self.vocab_files_names["specials_file"]
- )
- specials_dict_path = os.path.join(
- save_directory, pre + self.vocab_files_names["specials_dict_file"]
- )
- with open(vocab_path, "w", encoding="utf-8") as f:
- json.dump(self.math_token_to_rawid, f, ensure_ascii=False, indent=2)
- # Save both the flat list ("all") and the category dict (preferred)
- with open(specials_path, "w", encoding="utf-8") as f:
- json.dump(self.special_tokens_list, f, ensure_ascii=False, indent=2)
- with open(specials_dict_path, "w", encoding="utf-8") as f:
- json.dump(self.special_tokens, f, ensure_ascii=False, indent=2)
- return (vocab_path, specials_path)
-
-
-class SuryaOCRTokenizer(S3DownloaderMixin, PreTrainedTokenizer):
- def __init__(
- self,
- special_tokens: Dict[str, list] | None = None,
- model_checkpoint: str = settings.FOUNDATION_MODEL_CHECKPOINT,
- **kwargs,
- ):
- if special_tokens is None:
- special_tokens = dict()
-
- self.special_tokens = special_tokens
-
- self.ocr_tokenizer = GreedyMathUTF16Tokenizer.from_pretrained(
- model_checkpoint,
- )
- self.system_tokens = {
- v: self.ocr_tokenizer(v)["input_ids"][0]
- for v in special_tokens.get("system", [])
- }
- self.SPECIAL_TOKEN_MAPPING = self.ocr_tokenizer.SPECIAL_TOKEN_MAPPING
-
- super().__init__(**kwargs)
-
- def get_vocab(self) -> Dict[str, int]:
- return self.ocr_tokenizer.get_vocab()
-
- def _add_tokens(
- self,
- new_tokens: Union[List[str], List[AddedToken]],
- special_tokens: bool = False,
- ) -> int:
- return self.ocr_tokenizer._add_tokens(
- new_tokens, special_tokens=special_tokens
- )
-
- @property
- def vocab_size(self):
- return self.ocr_tokenizer.vocab_size
-
- def _tokenize(self, text: str, **kwargs):
- # task = kwargs.get("task", TaskNames.ocr_with_boxes)
- # assert task in TASK_NAMES, f"Invalid task: {task}"
-
- tokens = self.ocr_tokenizer(text)["input_ids"]
-
- return tokens
-
- def __call__(
- self,
- texts: Union[str, List[str]],
- tasks: Union[str, List[str]] = None,
- **kwargs,
- ) -> Dict[str, List[List[int]]]:
- """Tokenizes text and returns input IDs."""
- tokenized = []
-
- if isinstance(texts, str):
- texts = [texts]
- assert isinstance(tasks, str), "Tasks must be a string if texts is a string"
- tasks = [tasks]
-
- if isinstance(texts, list):
- assert isinstance(tasks, list), "Tasks must be a list if texts is a list"
-
- for text, task in zip(texts, tasks):
- tokens = self._tokenize(text, task=task)
- tokenized.append(tokens)
-
- return {"input_ids": tokenized}
-
- def decode(self, token_ids, **kwargs):
- if isinstance(token_ids, (np.ndarray, torch.Tensor)):
- token_ids = token_ids.tolist()
-
- decoded_text = self.ocr_tokenizer.decode(token_ids, skip_special_tokens=False)
- # replace all tokens with empty strings
- decoded_text = re.sub(r"", "", decoded_text)
- # replace with empty string
- decoded_text = re.sub(r"", "", decoded_text)
- return decoded_text
diff --git a/surya/common/surya/schema.py b/surya/common/surya/schema.py
deleted file mode 100644
index daabb620..00000000
--- a/surya/common/surya/schema.py
+++ /dev/null
@@ -1,15 +0,0 @@
-class TaskNames:
- block_without_boxes = "block_without_boxes"
- ocr_with_boxes = "ocr_with_boxes"
- ocr_without_boxes = "ocr_without_boxes"
- layout = "layout"
- table_structure = "table_structure"
-
-
-TASK_NAMES = [
- TaskNames.block_without_boxes,
- TaskNames.ocr_with_boxes,
- TaskNames.ocr_without_boxes,
- TaskNames.layout,
- TaskNames.table_structure,
-]
diff --git a/surya/common/util.py b/surya/common/util.py
index 4d7b4bea..bd093f57 100644
--- a/surya/common/util.py
+++ b/surya/common/util.py
@@ -1,9 +1,4 @@
-import copy
from typing import List
-import torch
-from functools import lru_cache
-
-import torch.nn.functional as F
from surya.common.polygon import PolygonBox
@@ -38,21 +33,6 @@ def clean_boxes(boxes: List[PolygonBox]) -> List[PolygonBox]:
return new_boxes
-def rescale_bbox(bbox, processor_size, image_size):
- page_width, page_height = processor_size
-
- img_width, img_height = image_size
- width_scaler = img_width / page_width
- height_scaler = img_height / page_height
-
- new_bbox = copy.deepcopy(bbox)
- new_bbox[0] = int(new_bbox[0] * width_scaler)
- new_bbox[1] = int(new_bbox[1] * height_scaler)
- new_bbox[2] = int(new_bbox[2] * width_scaler)
- new_bbox[3] = int(new_bbox[3] * height_scaler)
- return new_bbox
-
-
def expand_bbox(bbox, expansion_factor=0.01):
expansion_low = 1 - expansion_factor
expansion_high = 1 + expansion_factor
@@ -62,210 +42,3 @@ def expand_bbox(bbox, expansion_factor=0.01):
bbox[2] * expansion_high,
bbox[3] * expansion_high,
]
-
-SCRIPT_TOKEN_MAPPING = {
- "latin": "",
- "punctuation": "",
- "cyrillic": "",
- "arabic": "",
- "chinese": "",
- "japanese": "",
- "korean": "",
- "symbols": "",
- "greek": "",
- "armenian": "",
- "hebrew": "",
- "devanagari": "",
- "bengali": "",
- "gurmukhi": "",
- "gujarati": "",
- "oriya": "",
- "tamil": "",
- "telugu": "",
- "kannada": "",
- "malayalam": "",
- "sinhala": "",
- "thai": "",
- "lao": "",
- "myanmar": "",
- "georgian": "",
- "ethiopic": "",
- "khmer": "",
- "mongolian": "",
- "math": "",
-}
-
-@lru_cache(maxsize=1)
-def script_ranges():
- script_categories = {
- # Latin-based scripts (used by English, French, German, etc.)
- "latin": [
- (0x0041, 0x005A), # Latin uppercase A-Z
- (0x0061, 0x007A), # Latin lowercase a-z
- (0x0080, 0x00FF), # Latin-1 Supplement
- (0x0100, 0x017F), # Latin Extended-A
- (0x0180, 0x024F), # Latin Extended-B
- (0x0250, 0x02AF), # IPA Extensions
- (0x02B0, 0x02FF), # Spacing Modifier Letters
- (0x0300, 0x036F), # Combining Diacritical Marks
- (0x1E00, 0x1EFF), # Latin Extended Additional
- (0x2C60, 0x2C7F), # Latin Extended-C
- (0xA720, 0xA7FF), # Latin Extended-D
- ],
- # Punctuation, universal characters, and general symbols
- "punctuation": [
- (0x0020, 0x0020), # Space
- (0x0021, 0x002F), # Basic punctuation and symbols
- (0x0030, 0x0039), # Digits 0-9
- (0x003A, 0x0040), # More punctuation and symbols
- (0x005B, 0x0060), # More punctuation and symbols
- (0x007B, 0x007F), # More punctuation and symbols
- (0x2000, 0x206F), # General Punctuation
- ],
- # Cyrillic scripts (used by Russian, Ukrainian, etc.)
- "cyrillic": [
- (0x0400, 0x04FF), # Cyrillic
- (0x0500, 0x052F), # Cyrillic Supplement
- ],
- # Arabic scripts
- "arabic": [
- (0x0600, 0x06FF), # Arabic
- (0x0750, 0x077F), # Arabic Supplement
- (0x08A0, 0x08FF), # Arabic Extended-A
- ],
- # Chinese characters
- "chinese": [
- (0x4E00, 0x9FFF), # Common CJK Unified Ideographs
- (0x3400, 0x4DBF), # CJK Extension A
- (0x20000, 0x2A6DF), # CJK Extension B
- ],
- # Japanese-specific scripts (excluding shared CJK)
- "japanese": [
- (0x3040, 0x30FF), # Hiragana and Katakana
- ],
- # Korean-specific scripts
- "korean": [
- (0x1100, 0x11FF), # Hangul Jamo
- (0x3130, 0x318F), # Hangul Compatibility Jamo
- (0xAC00, 0xD7AF), # Hangul Syllables
- ],
- # Various mathematical and technical symbols
- "symbols": [
- (0x2070, 0x209F), # Superscripts and Subscripts
- (0x20A0, 0x20CF), # Currency Symbols
- (0x2100, 0x214F), # Letterlike Symbols
- (0x2150, 0x218F), # Number Forms
- (0x2190, 0x21FF), # Arrows
- (0x2200, 0x22FF), # Mathematical Operators
- (0x2300, 0x23FF), # Miscellaneous Technical
- (0x2500, 0x257F), # Box Drawing
- (0x2580, 0x259F), # Block Elements
- (0x25A0, 0x25FF), # Geometric Shapes
- (0x2600, 0x26FF), # Miscellaneous Symbols
- (0x2700, 0x27BF), # Dingbats
- (0x27C0, 0x27EF), # Miscellaneous Mathematical Symbols-A
- (0x2980, 0x29FF), # Miscellaneous Mathematical Symbols-B
- (0x2A00, 0x2AFF), # Supplemental Mathematical Operators
- (0x1D400, 0x1D7FF), # Mathematical Alphanumeric Symbols
- ],
- # Individual scripts for languages with unique writing systems
- "greek": [(0x0370, 0x03FF)], # Greek and Coptic
- "armenian": [(0x0530, 0x058F)], # Armenian
- "hebrew": [(0x0590, 0x05FF)], # Hebrew
- "devanagari": [(0x0900, 0x097F)], # Devanagari (Hindi, Sanskrit)
- "bengali": [(0x0980, 0x09FF)], # Bengali
- "gurmukhi": [(0x0A00, 0x0A7F)], # Gurmukhi (Punjabi)
- "gujarati": [(0x0A80, 0x0AFF)], # Gujarati
- "oriya": [(0x0B00, 0x0B7F)], # Oriya
- "tamil": [(0x0B80, 0x0BFF)], # Tamil
- "telugu": [(0x0C00, 0x0C7F)], # Telugu
- "kannada": [(0x0C80, 0x0CFF)], # Kannada
- "malayalam": [(0x0D00, 0x0D7F)], # Malayalam
- "sinhala": [(0x0D80, 0x0DFF)], # Sinhala
- "thai": [(0x0E00, 0x0E7F)], # Thai
- "lao": [(0x0E80, 0x0EFF)], # Lao
- "myanmar": [(0x1000, 0x109F)], # Myanmar
- "georgian": [(0x10A0, 0x10FF)], # Georgian
- "ethiopic": [(0x1200, 0x137F)], # Ethiopic
- "khmer": [(0x1780, 0x17FF)], # Khmer
- "mongolian": [(0x1800, 0x18AF)], # Mongolian
- }
-
- # Convert to a flat structure with character ranges
- flat_ranges = {}
- for category, ranges in script_categories.items():
- # Create a set of all characters in this category
- char_set = set()
- for start, end in ranges:
- char_set.update(range(start, end + 1))
-
- # Store the set in flat_ranges
- flat_ranges[category] = char_set
-
- return script_categories, flat_ranges
-
-def get_top_scripts(text: str, max_scripts: int = 5):
- script_categories, flat_ranges = script_ranges()
- char_count = {category: 0 for category in script_categories.keys()}
- for char in text:
- for category, char_set in flat_ranges.items():
- if ord(char) in char_set:
- char_count[category] += 1
- break
-
- top_scripts = sorted(char_count.items(), key=lambda x: x[1], reverse=True)
- top_scripts = [ts[0] for ts in top_scripts if ts[1] > 0]
- if " bool:
- if not torch.cuda.is_available():
- return False
-
- if "cuda" not in str(device):
- return False
-
- # Check CUDA version >= 12.0
- cuda_version_str = torch.version.cuda
- if cuda_version_str is None:
- return False
- cuda_version = tuple(map(int, cuda_version_str.split(".")))
- if cuda_version < (12, 0):
- return False
-
- # Check GPU compute capability (Ampere, Ada, Hopper GPUs)
- major, minor = torch.cuda.get_device_capability()
- compute_capability = major + minor / 10
- if compute_capability < 8.0:
- return False
-
- return True
-
-
-def pad_to_batch_size_repeat(tensor: torch.Tensor, batch_size: int):
- current_batch_size = tensor.shape[0]
- if current_batch_size >= batch_size:
- return tensor
-
- pad_size = batch_size - current_batch_size
- if pad_size < 0:
- return tensor
-
- # Repeat the last row pad_size times
- last_row = tensor[-1:].repeat(pad_size, 1, 1)
-
- # Concatenate original tensor with repeated last rows
- return torch.cat([tensor, last_row], dim=0)
-
-
-def pad_to_batch_size(tensor: torch.Tensor, batch_size: int):
- current_batch_size = tensor.shape[0]
- if current_batch_size >= batch_size:
- return tensor
-
- pad_size = batch_size - current_batch_size
- padding = (0, 0) * (tensor.dim() - 1) + (0, pad_size)
-
- return F.pad(tensor, padding, mode="constant", value=0)
diff --git a/surya/common/xla.py b/surya/common/xla.py
deleted file mode 100644
index 1ca40fef..00000000
--- a/surya/common/xla.py
+++ /dev/null
@@ -1,27 +0,0 @@
-import math
-from surya.settings import settings
-
-if settings.TORCH_DEVICE_MODEL == "xla":
- import torch_xla.core.xla_model as xm
-else:
- xm = None
-
-
-def get_nearest_pad(
- length: int, pad_multiple: int = settings.FOUNDATION_PAD_TO_NEAREST
-):
- return math.ceil(length / pad_multiple) * pad_multiple
-
-
-def get_compile_args(device: str) -> dict:
- if not settings.FOUNDATION_XLA:
- return {}
-
- return {
- "backend": "openxla",
- }
-
-
-def mark_step():
- if xm is not None:
- xm.mark_step()
diff --git a/surya/debug/text.py b/surya/debug/text.py
index 777d8d19..ce120e26 100644
--- a/surya/debug/text.py
+++ b/surya/debug/text.py
@@ -1,24 +1,4 @@
-import re
-from io import BytesIO
-from typing import List, Tuple
-from PIL import Image, ImageDraw, ImageFont
-
-from surya.debug.fonts import get_font_path
-from surya.debug.render_html import render_text_as_html
-
-try:
- from playwright.sync_api import sync_playwright
-
- has_playwright = True
-except ImportError:
- has_playwright = False
-
-
-def strip_html_tags(html_text):
- pattern = re.compile(r"<[\w/][^>]*>")
- text_only = pattern.sub("", html_text)
-
- return text_only
+from PIL import Image, ImageDraw
def get_text_size(text, font):
@@ -26,75 +6,3 @@ def get_text_size(text, font):
draw = ImageDraw.Draw(im)
_, _, width, height = draw.textbbox((0, 0), text=text, font=font)
return width, height
-
-
-def render_text(draw, text, s_bbox, bbox_width, bbox_height, font_path, box_font_size):
- font = ImageFont.truetype(font_path, box_font_size)
- text_width, text_height = get_text_size(text, font)
- while (text_width > bbox_width or text_height > bbox_height) and box_font_size > 6:
- box_font_size = box_font_size - 1
- font = ImageFont.truetype(font_path, box_font_size)
- text_width, text_height = get_text_size(text, font)
-
- # Calculate text position (centered in bbox)
- text_width, text_height = get_text_size(text, font)
- x = s_bbox[0]
- y = s_bbox[1] + (bbox_height - text_height) / 2
-
- draw.text((x, y), text, fill="black", font=font)
-
-
-def draw_text_with_playwright(
- bboxes, texts: List[str], image_size: Tuple[int, int]
-) -> Image.Image:
- html_content, image_size = render_text_as_html(bboxes, texts, image_size)
- if not has_playwright:
- raise ImportError(
- "Playwright is not installed. Please install it using `pip install playwright`"
- )
-
- with sync_playwright() as p:
- browser = p.chromium.launch(headless=True)
- page = browser.new_page(
- viewport={"width": image_size[0], "height": image_size[1]}
- )
- page.set_content(html_content)
- page.wait_for_timeout(1000)
- body = page.query_selector("body")
- image = body.screenshot()
- browser.close()
-
- pil_img = Image.open(BytesIO(image))
- return pil_img
-
-
-def draw_text_on_image(
- bboxes,
- texts,
- image_size: Tuple[int, int],
- font_path=None,
- max_font_size=60,
- res_upscale=2,
-) -> Image.Image:
- if has_playwright:
- return draw_text_with_playwright(bboxes, texts, image_size)
-
- texts = [strip_html_tags(text) for text in texts]
- if font_path is None:
- font_path = get_font_path()
- new_image_size = (image_size[0] * res_upscale, image_size[1] * res_upscale)
- image = Image.new("RGB", new_image_size, color="white")
- draw = ImageDraw.Draw(image)
-
- for bbox, text in zip(bboxes, texts):
- s_bbox = [int(coord * res_upscale) for coord in bbox]
- bbox_width = s_bbox[2] - s_bbox[0]
- bbox_height = s_bbox[3] - s_bbox[1]
-
- # Shrink the text to fit in the bbox if needed
- box_font_size = max(6, min(int(0.75 * bbox_height), max_font_size))
- render_text(
- draw, text, s_bbox, bbox_width, bbox_height, font_path, box_font_size
- )
-
- return image
diff --git a/surya/detection/__init__.py b/surya/detection/__init__.py
index 7715b4ba..298bceb4 100644
--- a/surya/detection/__init__.py
+++ b/surya/detection/__init__.py
@@ -9,7 +9,6 @@
from tqdm import tqdm
from surya.common.predictor import BasePredictor
-from surya.common.xla import mark_step
from surya.detection.loader import DetectionModelLoader
from surya.detection.parallel import FakeExecutor
@@ -22,14 +21,12 @@
class DetectionPredictor(BasePredictor):
model_loader_cls = DetectionModelLoader
batch_size = settings.DETECTOR_BATCH_SIZE
- default_batch_sizes = {"cpu": 8, "mps": 8, "cuda": 36, "xla": 18}
+ default_batch_sizes = {"cpu": 8, "mps": 8, "cuda": 36}
def __call__(
self, images: List[Image.Image], batch_size=None, include_maps=False
) -> List[TextDetectionResult]:
- detection_generator = self.batch_detection(
- images, batch_size=batch_size, static_cache=settings.DETECTOR_STATIC_CACHE
- )
+ detection_generator = self.batch_detection(images, batch_size=batch_size)
postprocessing_futures = []
max_workers = min(settings.DETECTOR_POSTPROCESSING_CPU_WORKERS, len(images))
@@ -62,7 +59,7 @@ def prepare_image(self, img):
return img
def batch_detection(
- self, images: List, batch_size=None, static_cache=False
+ self, images: List, batch_size=None
) -> Generator[Tuple[List[List[np.ndarray]], List[Tuple[int, int]]], None, None]:
assert all([isinstance(image, Image.Image) for image in images])
if batch_size is None:
@@ -109,13 +106,9 @@ def batch_detection(
image_splits = [self.prepare_image(image) for image in image_splits]
# Batch images in dim 0
batch = torch.stack(image_splits, dim=0).to(self.model.dtype)
- if static_cache:
- batch = self.pad_to_batch_size(batch, batch_size)
with settings.INFERENCE_MODE():
- pred = self.model(
- pixel_values=batch.to(self.model.device)
- ) # Moving the to device here fixes issues with xla recompilation
+ pred = self.model(pixel_values=batch.to(self.model.device))
logits = pred.logits
correct_shape = [
@@ -127,7 +120,6 @@ def batch_detection(
logits = F.interpolate(
logits, size=correct_shape, mode="bilinear", align_corners=False
)
- mark_step()
logits = logits.to(torch.float32).cpu().numpy()
preds = []
diff --git a/surya/detection/loader.py b/surya/detection/loader.py
index ac1ee4cc..189ed0e6 100644
--- a/surya/detection/loader.py
+++ b/surya/detection/loader.py
@@ -40,16 +40,6 @@ def model(
model = model.to(device)
model = model.eval()
- if settings.COMPILE_ALL or settings.COMPILE_DETECTOR:
- torch._dynamo.config.cache_size_limit = 1
- torch._dynamo.config.suppress_errors = False
-
- logger.info(
- f"Compiling detection model {self.checkpoint} on device {device} with dtype {dtype}"
- )
- compile_args = {"backend": "openxla"} if device == "xla" else {}
- model = torch.compile(model, **compile_args)
-
logger.debug(
f"Loaded detection model {self.checkpoint} from {EfficientViTForSemanticSegmentation.get_local_path(self.checkpoint)} onto device {device} with dtype {dtype}"
)
diff --git a/surya/detection/processor.py b/surya/detection/processor.py
index e8a8b709..1cb44a0c 100644
--- a/surya/detection/processor.py
+++ b/surya/detection/processor.py
@@ -19,7 +19,11 @@
import numpy as np
-from transformers.image_processing_utils import BaseImageProcessor, BatchFeature, get_size_dict
+from transformers.image_processing_utils import (
+ BaseImageProcessor,
+ BatchFeature,
+ get_size_dict,
+)
from transformers.image_transforms import to_channel_dimension_format
from transformers.image_utils import (
IMAGENET_DEFAULT_MEAN,
@@ -34,7 +38,6 @@
import PIL.Image
-import torch
from surya.common.s3 import S3DownloaderMixin
@@ -107,7 +110,9 @@ def __init__(
self.do_rescale = do_rescale
self.rescale_factor = rescale_factor
self.do_normalize = do_normalize
- self.image_mean = image_mean if image_mean is not None else IMAGENET_DEFAULT_MEAN
+ self.image_mean = (
+ image_mean if image_mean is not None else IMAGENET_DEFAULT_MEAN
+ )
self.image_std = image_std if image_std is not None else IMAGENET_DEFAULT_STD
self.do_reduce_labels = do_reduce_labels
self._valid_processor_keys = [
@@ -152,12 +157,18 @@ def _preprocess(
image_std: Optional[Union[float, List[float]]] = None,
input_data_format: Optional[Union[str, ChannelDimension]] = None,
):
-
if do_rescale:
- image = self.rescale(image=image, scale=rescale_factor, input_data_format=input_data_format)
+ image = self.rescale(
+ image=image, scale=rescale_factor, input_data_format=input_data_format
+ )
if do_normalize:
- image = self.normalize(image=image, mean=image_mean, std=image_std, input_data_format=input_data_format)
+ image = self.normalize(
+ image=image,
+ mean=image_mean,
+ std=image_std,
+ input_data_format=input_data_format,
+ )
return image
@@ -193,7 +204,9 @@ def _preprocess_image(
input_data_format=input_data_format,
)
if data_format is not None:
- image = to_channel_dimension_format(image, data_format, input_channel_dim=input_data_format)
+ image = to_channel_dimension_format(
+ image, data_format, input_channel_dim=input_data_format
+ )
return image
def __call__(self, images, segmentation_maps=None, **kwargs):
@@ -276,7 +289,9 @@ def preprocess(
do_normalize = do_normalize if do_normalize is not None else self.do_normalize
resample = resample if resample is not None else self.resample
size = size if size is not None else self.size
- rescale_factor = rescale_factor if rescale_factor is not None else self.rescale_factor
+ rescale_factor = (
+ rescale_factor if rescale_factor is not None else self.rescale_factor
+ )
image_mean = image_mean if image_mean is not None else self.image_mean
image_std = image_std if image_std is not None else self.image_std
@@ -299,4 +314,4 @@ def preprocess(
]
data = {"pixel_values": images}
- return BatchFeature(data=data, tensor_type=return_tensors)
\ No newline at end of file
+ return BatchFeature(data=data, tensor_type=return_tensors)
diff --git a/surya/foundation/__init__.py b/surya/foundation/__init__.py
deleted file mode 100644
index ada34b6b..00000000
--- a/surya/foundation/__init__.py
+++ /dev/null
@@ -1,915 +0,0 @@
-from __future__ import annotations
-
-from dataclasses import dataclass
-from typing import List, Optional, Tuple
-from collections import deque
-
-import cv2
-import numpy as np
-import torch
-import math
-from PIL import Image
-from tqdm import tqdm
-import torch.nn.functional as F
-
-from surya.common.surya import SuryaModelOutput
-from surya.common.xla import mark_step
-from surya.common.predictor import BasePredictor
-
-from surya.foundation.loader import FoundationModelLoader
-from surya.foundation.util import (
- detect_repeat_token,
-)
-from surya.common.surya.schema import TaskNames
-from surya.foundation.cache.dynamic_ops import DynamicOpsCache
-from surya.foundation.cache.static_ops import StaticOpsCache
-
-from surya.settings import settings
-from surya.logging import get_logger, configure_logging
-
-configure_logging()
-logger = get_logger()
-
-
-@dataclass
-class ContinuousBatchInput:
- input_ids: torch.Tensor
- input_boxes: torch.Tensor
- position_ids: torch.Tensor
- # input_ids and position_ids may be padded, num_valid_tokens tracks the 'real' counts
- num_valid_tokens: torch.Tensor
- # count the number of predicted tokens for each batch element so far
- num_predicted_tokens: torch.Tensor
- needs_bbox_embedding: torch.Tensor
-
-
-@dataclass
-class ContinuousBatchOutput:
- input_ids: torch.Tensor
- preds: torch.Tensor
- bbox_preds: torch.Tensor
- scores: torch.Tensor
- token_probs: torch.Tensor
-
-
-@dataclass
-class FoundationPrompt:
- id: int
- task_name: TaskNames
- image: np.ndarray
- text: str
- math_mode: bool
-
-
-class FoundationPredictor(BasePredictor):
- model_loader_cls = FoundationModelLoader
- batch_size = (
- settings.RECOGNITION_BATCH_SIZE
- ) # Default to the recognition batch size
- torch_dtype = None # No default, loader picks the dtype based on device properties - bf16/fp16
- default_batch_sizes = {"cpu": 32, "mps": 64, "cuda": 256, "xla": 64}
- encoder_chunk_size: int = 4096 # Default chunk size
- encoder_chunk_sizes = {"cpu": 4096, "mps": 4096, "cuda": 32768, "xla": 32768}
- extra_token_count = {
- "xla": 128
- } # We have to pad the XLA cache since we don't use sliding window
- min_prefill_ratio: int = 1 if settings.FOUNDATION_XLA else 0.2
- min_trim_length: int = 50
- tasks = {
- TaskNames.ocr_with_boxes: {
- "needs_bboxes": True,
- "img_size": (1024, 512),
- "max_tokens": 224,
- },
- TaskNames.ocr_without_boxes: {
- "needs_bboxes": False,
- "img_size": (1024, 512),
- "max_tokens": 224,
- },
- TaskNames.block_without_boxes: {
- "needs_bboxes": False,
- "img_size": (1024, 512),
- "max_tokens": 768,
- },
- TaskNames.layout: {
- "needs_bboxes": False,
- "img_size": (1024, 1024),
- "max_tokens": 200,
- },
- TaskNames.table_structure: {
- "needs_bboxes": False,
- "img_size": (1024, 512),
- "max_tokens": 600,
- },
- }
-
- def __init__(
- self,
- checkpoint=None,
- device=settings.TORCH_DEVICE_MODEL,
- dtype=None,
- attention_implementation: Optional[str] = None,
- ):
- super().__init__(checkpoint, device, dtype, attention_implementation)
- self.prompt_queue = deque()
- self.batch_prompt_mapping = None
- self.kv_cache = None
-
- self.beacon_token_interval = self.model.config.beacon_token_interval
-
- # Setup various tokens on-device
- self.device_pad_token = torch.tensor(
- self.processor.pad_token_id, device=self.model.device, dtype=torch.long
- )
- self.device_beacon_token = torch.tensor(
- self.processor.beacon_token_id, device=self.model.device, dtype=torch.long
- )
- self.special_token_ids = torch.tensor(
- [self.model.config.image_token_id] + self.model.config.register_token_ids,
- device=self.model.device,
- )
-
- self.pad_to_multiple = (
- settings.FOUNDATION_PAD_TO_NEAREST
- if settings.FOUNDATION_STATIC_CACHE
- else None
- )
-
- def to(self, device_dtype: torch.device | str | None = None):
- super().to(device_dtype)
- self.special_token_ids = self.special_token_ids.to(device_dtype)
-
- def get_encoder_chunk_size(self) -> int:
- if settings.FOUNDATION_CHUNK_SIZE is not None:
- return settings.FOUNDATION_CHUNK_SIZE
-
- chunk_size = self.encoder_chunk_size
- if settings.TORCH_DEVICE_MODEL in self.encoder_chunk_sizes:
- if settings.TORCH_DEVICE_MODEL in self.encoder_chunk_sizes:
- chunk_size = self.encoder_chunk_sizes[settings.TORCH_DEVICE_MODEL]
- return chunk_size
-
- def setup_cache(self, batch_size: int, max_cache_len: int, max_sliding_window: int):
- kv_cache_cls = StaticOpsCache if settings.FOUNDATION_XLA else DynamicOpsCache
- self.kv_cache = kv_cache_cls(
- self.model.config,
- batch_size,
- max_cache_len,
- text_sliding_window=max_sliding_window,
- device=self.model.device,
- dtype=self.model.dtype,
- )
- self.prompt_queue.clear()
- self.batch_prompt_mapping = {i: None for i in range(batch_size)}
-
- @property
- def num_empty_slots(self):
- return sum(v is None for v in self.batch_prompt_mapping.values())
-
- @property
- def num_active_slots(self):
- return len(self.batch_prompt_mapping) - self.num_empty_slots
-
- def prepare_input(
- self,
- task_names: List[str],
- images: List[Image.Image],
- input_text: List[str | None],
- math_modes: List[bool],
- ):
- batch = []
- for image, text, task_name, math_mode in zip(
- images, input_text, task_names, math_modes
- ):
- image_size = self.tasks[task_name]["img_size"]
-
- try:
- image = self.processor.scale_to_fit(
- image, image_size
- ) # Only resizes if out of bounds (max/min)
- except cv2.error:
- # The image is empty if it can't be resized, so just make a blank image
- image = np.zeros((image_size[1], image_size[0], 3), dtype=np.float32)
-
- # Task input is the same for all tasks for now
- text = text or ""
-
- # Remove input text that exceeds max generation tokens (likely invalid)
- if len(text) > self.tasks[task_name]["max_tokens"]:
- text = ""
- inputs = [
- {"type": "image", "image": image, "rotated": False},
- {"type": "text", "text": text.strip(), "math": math_mode},
- ]
- batch.append({"task": task_name, "inputs": inputs})
-
- return batch
-
- def process_outputs(
- self, outputs: SuryaModelOutput, max_lookahead_tokens: Optional[int] = None
- ) -> ContinuousBatchOutput:
- # Predictions are multi-token
- lm_logits = outputs["lm_logits"].float() # shape: [batch_size, seq_len, V]
- bbox_logits = outputs["bbox_logits"].float() # shape: [batch_size, seq_len, 6]
-
- if (
- max_lookahead_tokens is not None
- and lm_logits.shape[1] > max_lookahead_tokens + 1
- ):
- lm_logits = lm_logits[:, : max_lookahead_tokens + 1, :]
- bbox_logits = bbox_logits[:, : max_lookahead_tokens + 1, :]
-
- # Get predictions
- preds = torch.argmax(lm_logits, dim=-1)
- input_ids = preds.to(torch.long)
-
- # Confidence scores for all tokens
- token_probs = F.softmax(lm_logits, dim=-1)
- scores = torch.max(token_probs, dim=-1).values # shape: [B, T]
-
- # Update input boxes
- box_preds = bbox_logits * self.model.config.bbox_size
- box_preds = box_preds.to(torch.long)
-
- return ContinuousBatchOutput(
- input_ids=input_ids,
- preds=preds,
- bbox_preds=box_preds,
- scores=scores,
- token_probs=token_probs,
- )
-
- # Always left pad with beacons, don't worry about attention masking
- def maybe_insert_beacon_tokens(
- self,
- input_ids: torch.Tensor,
- input_boxes: torch.Tensor,
- num_predicted_tokens: torch.Tensor,
- num_new_tokens: Optional[torch.Tensor] = None,
- ) -> Tuple[torch.Tensor, torch.Tensor]:
- batch_size, seq_len = (
- input_ids.shape
- ) # seq_len can be >1 - In case of multi-token predictions
-
- # num_predicted tokens **does not include** the current new input_ids, this number is updated **after beacon tokens are inserted**
- token_positions = num_predicted_tokens + torch.arange(
- 1, seq_len + 1, device=input_ids.device
- ).unsqueeze(0)
- beacon_positions = token_positions % self.beacon_token_interval == 0
-
- # If no beacons needed, return original input
- needs_beacon = beacon_positions.any(dim=1) # shape: [batch_size]
- if not needs_beacon.any():
- if num_new_tokens is None:
- num_new_tokens = (
- torch.ones(batch_size, dtype=torch.long, device=input_ids.device)
- * seq_len
- )
- return input_ids, input_boxes, num_new_tokens.squeeze(1)
-
- beacon_insert_pos = torch.zeros(
- batch_size, dtype=torch.long, device=input_ids.device
- )
- for i in range(batch_size):
- if needs_beacon[i]:
- # Find first position that needs beacon
- beacon_insert_pos[i] = torch.where(beacon_positions[i])[0]
-
- # Padded input ids.
- new_input_ids = torch.full(
- (batch_size, seq_len + 1),
- self.device_pad_token,
- dtype=input_ids.dtype,
- device=input_ids.device,
- )
- new_input_boxes = torch.full(
- (batch_size, seq_len + 1, 6),
- -100,
- dtype=input_boxes.dtype,
- device=input_boxes.device,
- )
- # Fill in tokens for each sequence
- for i in range(batch_size):
- if needs_beacon[i]:
- insert_pos = beacon_insert_pos[i]
- new_input_ids[i, insert_pos] = self.device_beacon_token
- new_input_boxes[i, insert_pos, :] = -100
- if insert_pos > 0:
- new_input_ids[i, :insert_pos] = input_ids[i, :insert_pos]
- new_input_boxes[i, :insert_pos] = input_boxes[i, :insert_pos]
- new_input_ids[i, insert_pos + 1 :] = input_ids[i, insert_pos:]
- new_input_boxes[i, insert_pos + 1 :] = input_boxes[i, insert_pos:]
- else:
- new_input_ids[i, 1:] = input_ids[i, :]
- new_input_boxes[i, 1:] = input_boxes[i, :]
-
- # Calculate valid token counts for both padded and non padded sequences
- valid_token_counts = torch.where(
- needs_beacon,
- torch.tensor(seq_len + 1, device=input_ids.device),
- torch.tensor(seq_len, device=input_ids.device),
- )
-
- return new_input_ids, new_input_boxes, valid_token_counts
-
- def decode(
- self,
- current_inputs: Optional[ContinuousBatchInput] = None,
- max_lookahead_tokens: Optional[int] = None,
- ):
- # Note - If we want to use the outputs from the non-last token, we
- # need to set the cache position manually to ensure causality. The default
- # behavior only works for the last token currently
- input_ids = current_inputs.input_ids
- input_boxes = current_inputs.input_boxes
- embed_boxes = current_inputs.needs_bbox_embedding
-
- position_ids = current_inputs.position_ids
- num_predicted_tokens = current_inputs.num_predicted_tokens
- num_valid_tokens = current_inputs.num_valid_tokens
- batch_size = input_ids.shape[0]
-
- # Pre-shift the attention mask based on the cache update
- self.kv_cache.decode_attention_mask_update(
- num_valid_tokens=num_valid_tokens, cache_idxs=list(range(batch_size))
- )
-
- cache_position = self.get_cache_position(
- input_ids.shape[1], self.kv_cache.attention_mask, prefill=False
- )
- with settings.INFERENCE_MODE():
- outputs = self.model(
- input_ids=input_ids,
- attention_mask=self.kv_cache.attention_mask,
- position_ids=position_ids,
- cache_position=cache_position,
- use_cache=True,
- past_key_values=self.kv_cache,
- prefill=False,
- num_valid_tokens=num_valid_tokens,
- input_boxes=input_boxes,
- embed_boxes=embed_boxes,
- logits_to_keep=1,
- )
-
- processed_output: ContinuousBatchOutput = self.process_outputs(
- outputs, max_lookahead_tokens=max_lookahead_tokens
- )
-
- input_ids = processed_output.input_ids
- input_boxes = processed_output.bbox_preds
-
- # Update this **before** inserting beacon tokens
- tau = settings.FOUNDATION_MULTI_TOKEN_MIN_CONFIDENCE
- if max_lookahead_tokens is not None:
- num_new_tokens = torch.clamp(
- (
- processed_output.scores.ge(tau)
- .to(torch.long)
- .cumprod(dim=1)
- .sum(dim=1, keepdim=True)
- ),
- min=1,
- )
- else:
- num_new_tokens = input_ids.shape[1]
-
- num_predicted_tokens += num_new_tokens
- input_ids, input_boxes, num_valid_tokens = self.maybe_insert_beacon_tokens(
- input_ids, input_boxes, num_predicted_tokens, num_new_tokens
- )
- position_ids = position_ids[:, -1:] + torch.arange(
- 1, input_ids.shape[1] + 1, device=input_ids.device
- )
- # Some of the input sequences may now have left padding tokens, so we want to account for that
- # offset is a per-batch offset of the position_ids
- offset = (input_ids.shape[1] - num_valid_tokens).unsqueeze(1)
- position_ids -= offset
-
- new_input = ContinuousBatchInput(
- input_ids=input_ids,
- input_boxes=input_boxes,
- position_ids=position_ids,
- num_valid_tokens=num_valid_tokens,
- num_predicted_tokens=num_predicted_tokens,
- needs_bbox_embedding=current_inputs.needs_bbox_embedding,
- )
-
- return new_input, processed_output
-
- def pad_and_shift_input_ids_position_ids(
- self,
- input_ids: torch.Tensor,
- bbox_preds: torch.Tensor,
- position_ids: torch.Tensor,
- new_seq_len: int,
- ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
- """
- Pads new_input_ids to match the new seq len with **left padding**
- and creates updated position_ids
-
- Returns:
- padded_input_ids (torch.Tensor): [batch_size, current_seq_len]
- updated_position_ids (torch.Tensor): [batch_size, current_seq_len]
- """
- # No padding
- if new_seq_len == input_ids.shape[1]:
- return (
- input_ids,
- bbox_preds,
- position_ids[:, -1:] + torch.arange(1, new_seq_len + 1, device=self.model.device),
- )
-
- pad_len = new_seq_len - input_ids.shape[1]
- padded_input_ids = torch.nn.functional.pad(
- input_ids, (pad_len, 0), value=self.device_pad_token
- )
-
- padded_bbox_preds = torch.nn.functional.pad(
- bbox_preds, (0, 0, pad_len, 0), value=-100
- )
-
- # Since we have **left padding**, offset the new position_ids by the amount of padding
- # This ensures that the **true tokens** get the correct position_ids
- # The position_ids assigned to pad tokens do not matter. They are not cached, and not used for outputs
- updated_position_ids = position_ids[:, -1:] + torch.arange(
- 1, new_seq_len + 1, device=self.model.device
- )
- updated_position_ids -= pad_len
-
- return padded_input_ids, padded_bbox_preds, updated_position_ids
-
- def get_cache_position(
- self,
- seq_len: int,
- attention_mask: torch.Tensor,
- prefill: bool,
- ):
- batch_size, target_len = attention_mask.shape
- base_cache_position = (
- torch.arange(seq_len, device=attention_mask.device)
- .unsqueeze(0)
- .expand(batch_size, -1)
- )
- if prefill:
- return base_cache_position
-
- # This is a (batch_size) tensor, we can add the seq lens here
- cache_seqlens = (
- attention_mask
- * torch.arange(attention_mask.size(1), device=attention_mask.device)
- ).argmax(dim=1).to(torch.int32) + 1
- # Needs to be unsqueezed so broadcasting works
- return cache_seqlens.unsqueeze(1) + base_cache_position
-
- def prefill(
- self,
- current_inputs: Optional[ContinuousBatchInput] = None,
- max_lookahead_tokens: Optional[int] = None,
- ):
- logger.debug(f"Prefilling {self.num_empty_slots} slots")
-
- prompts: List[FoundationPrompt] = [
- self.prompt_queue.popleft()
- for _ in range(min(self.num_empty_slots, len(self.prompt_queue)))
- ]
- non_active_idxs = [k for k, v in self.batch_prompt_mapping.items() if v is None]
- idxs_to_merge = non_active_idxs[: len(prompts)]
-
- for i, prompt in zip(idxs_to_merge, prompts):
- self.batch_prompt_mapping[i] = prompt.id
-
- needs_bbox_embedding = torch.tensor(
- [
- p.task_name in [TaskNames.layout, TaskNames.table_structure]
- for p in prompts
- ],
- dtype=torch.bool,
- )
-
- batch_input = self.prepare_input(
- task_names=[p.task_name for p in prompts],
- images=[p.image for p in prompts],
- input_text=[p.text for p in prompts],
- math_modes=[
- p.math_mode for p in prompts
- ], # Pass math mode to the processor
- )
- processed_inputs = self.processor(
- batch_input,
- padding_side="left",
- device=self.model.device,
- pad_to_multiple=self.pad_to_multiple,
- )
-
- input_ids = processed_inputs["input_ids"].to(dtype=torch.long)
- attention_mask = processed_inputs["attention_mask"].to(dtype=torch.long)
- position_ids = processed_inputs["position_ids"].to(dtype=torch.long)
- valid_batch_size = len(idxs_to_merge)
-
- # Keep these off device until later
- image_tiles = processed_inputs["image_tiles"].to(dtype=self.model.dtype)
- grid_thw = processed_inputs["grid_thw"].to(dtype=torch.long)
-
- if settings.FOUNDATION_STATIC_CACHE:
- input_ids = self.pad_to_batch_size(
- input_ids, batch_size=self.kv_cache.max_batch_size
- )
- attention_mask = self.pad_to_batch_size(
- attention_mask, batch_size=self.kv_cache.max_batch_size
- )
- position_ids = self.pad_to_batch_size(
- position_ids, batch_size=self.kv_cache.max_batch_size
- )
- needs_bbox_embedding = self.pad_to_batch_size(
- needs_bbox_embedding, batch_size=self.kv_cache.max_batch_size
- )
-
- # Move to device after padding
- input_ids = input_ids.to(device=self.model.device)
- attention_mask = attention_mask.to(device=self.model.device)
- position_ids = position_ids.to(device=self.model.device)
- needs_bbox_embedding = needs_bbox_embedding.to(device=self.model.device)
-
- # Find text lengths of each
- # Oddly, this is optimal on GPU - causes a 30% slowdown if "optimized"
- # Be very careful with the type and device of this - can cause
- # a big slowdown if put on device
- is_special = (
- (input_ids.unsqueeze(-1) == self.special_token_ids).any(-1).cpu()
- ) # (batch, seq_len)
- text_lengths = []
- for i in range(input_ids.shape[0]):
- special_positions = is_special[i].nonzero(as_tuple=True)[0]
- if len(special_positions) > 0:
- # Assuming special tokens are contiguous at the start
- prefix_len = special_positions[-1].item() + 1
- else:
- prefix_len = 0
- text_lengths.append(input_ids.shape[1] - prefix_len)
- text_lengths = torch.tensor(text_lengths, dtype=torch.long)
-
- cache_position = self.get_cache_position(
- input_ids.shape[1], attention_mask, prefill=True
- )
- with settings.INFERENCE_MODE():
- image_embeddings = self.model.get_image_embeddings(
- pixel_values=image_tiles,
- grid_thw=grid_thw,
- encoder_chunk_size=self.get_encoder_chunk_size(),
- valid_batch_size=valid_batch_size,
- max_batch_size=self.kv_cache.max_batch_size,
- )
- mark_step()
-
- outputs = self.model(
- input_ids=input_ids,
- image_embeddings=image_embeddings,
- attention_mask=attention_mask,
- position_ids=position_ids,
- cache_position=cache_position,
- inputs_embeds=None,
- past_key_values=self.kv_cache,
- use_cache=True,
- encoder_chunk_size=self.get_encoder_chunk_size(),
- cache_idxs=idxs_to_merge,
- prefill=True,
- num_valid_tokens=None, # Not required during prefill
- text_lengths=text_lengths,
- valid_batch_size=valid_batch_size,
- logits_to_keep=1,
- )
-
- # Process outputs
- processed_outputs = self.process_outputs(
- outputs, max_lookahead_tokens=max_lookahead_tokens
- )
- # Multi-token prediction
- predicted_tokens = processed_outputs.input_ids.shape[1]
- num_valid_tokens = (
- torch.ones((input_ids.shape[0]), device=self.model.device, dtype=torch.long)
- * predicted_tokens
- )
- num_predicted_tokens = (
- torch.ones(
- (input_ids.shape[0], 1), device=self.model.device, dtype=torch.long
- )
- * predicted_tokens
- )
-
- self.kv_cache.prefill_attention_mask_update(
- attention_mask, idxs_to_merge, valid_batch_size, text_lengths
- )
- self.kv_cache.update_text_counts(idxs_to_merge, valid_batch_size, text_lengths)
-
- full_batch = len(idxs_to_merge) == self.kv_cache.max_batch_size
-
- # If full batch, then we can ignore current_inputs
- if current_inputs is None or full_batch:
- new_seq_len = processed_outputs.input_ids.shape[1]
- # No padding tokens - So we can safely set position_ids this way
- position_ids = position_ids[:, -1:] + torch.arange(
- 1, new_seq_len + 1, device=position_ids.device
- )
- new_input = ContinuousBatchInput(
- input_ids=processed_outputs.input_ids,
- input_boxes=processed_outputs.bbox_preds,
- position_ids=position_ids,
- num_valid_tokens=num_valid_tokens,
- num_predicted_tokens=num_predicted_tokens,
- needs_bbox_embedding=needs_bbox_embedding,
- )
-
- return (
- new_input,
- processed_outputs,
- range(processed_outputs.input_ids.shape[0]),
- )
-
- # Merging inputs for next steps
- current_input_ids = current_inputs.input_ids
- current_position_ids = current_inputs.position_ids
- current_input_boxes = current_inputs.input_boxes
-
- current_needs_bbox_embedding = current_inputs.needs_bbox_embedding
-
- assert current_input_ids.shape[1] == current_position_ids.shape[1]
- input_ids, bbox_preds, position_ids = self.pad_and_shift_input_ids_position_ids(
- processed_outputs.input_ids,
- processed_outputs.bbox_preds,
- position_ids,
- new_seq_len=current_input_ids.shape[1],
- )
-
- current_input_ids[idxs_to_merge] = input_ids[:valid_batch_size]
- current_input_boxes[idxs_to_merge] = bbox_preds[:valid_batch_size]
- current_position_ids[idxs_to_merge] = position_ids[:valid_batch_size]
-
- current_num_valid_tokens = current_inputs.num_valid_tokens
- current_num_valid_tokens[idxs_to_merge] = num_valid_tokens[:valid_batch_size]
-
- current_num_predicted_tokens = current_inputs.num_predicted_tokens
- current_num_predicted_tokens[idxs_to_merge] = num_predicted_tokens[
- :valid_batch_size
- ]
- current_needs_bbox_embedding[idxs_to_merge] = needs_bbox_embedding[
- :valid_batch_size
- ]
-
- new_input = ContinuousBatchInput(
- input_ids=current_input_ids,
- input_boxes=current_input_boxes,
- position_ids=current_position_ids,
- num_valid_tokens=current_num_valid_tokens,
- num_predicted_tokens=current_num_predicted_tokens,
- needs_bbox_embedding=current_needs_bbox_embedding,
- )
-
- return new_input, processed_outputs, idxs_to_merge
-
- def get_max_image_token_count(
- self, images: list[np.ndarray], tasks: List[TaskNames]
- ) -> int:
- def compute_scaled_size(
- H: int, W: int, max_size: Tuple[int, int]
- ) -> Tuple[int, int]:
- max_W, max_H = max_size
- min_W, min_H = (168, 168)
-
- current_pixels = H * W
- max_pixels = max_H * max_W
- min_pixels = min_H * min_W
- current_pixels = max(1, current_pixels) # Avoid zero division
-
- if current_pixels > max_pixels:
- scale = (max_pixels / current_pixels) ** 0.5
- return math.floor(H * scale), math.floor(W * scale)
- elif current_pixels < min_pixels:
- scale = (min_pixels / current_pixels) ** 0.5
- return math.ceil(H * scale), math.ceil(W * scale)
- return H, W
-
- def get_tile_count(H: int, W: int, factor: int) -> int:
- H_bar = math.ceil(H / factor) * factor
- W_bar = math.ceil(W / factor) * factor
- grid_h = H_bar / self.processor.patch_size
- grid_w = W_bar // self.processor.patch_size
- return grid_h * grid_w
-
- max_tokens = 0
- factor = self.processor.patch_size * self.processor.merge_size
-
- for image, task in zip(images, tasks):
- H, W = image.shape[:2]
- max_size = self.tasks[task]["img_size"]
- scaled_H, scaled_W = compute_scaled_size(H, W, max_size)
- token_count = get_tile_count(scaled_H, scaled_W, factor) / (
- self.processor.merge_size**2
- )
- max_tokens = max(max_tokens, token_count)
-
- # Extra 10 to account for EOS/BOS/Rotation token etc.
- return 10 + self.processor.num_register_tokens + int(max_tokens)
-
- def prediction_loop(
- self,
- images: List[np.ndarray],
- input_texts: List[str],
- task_names: List[TaskNames],
- batch_size: int | None = None,
- max_tokens: int | None = None,
- max_sliding_window: int | None = None,
- math_mode: bool = True,
- drop_repeated_tokens: bool = True,
- max_lookahead_tokens: Optional[int] = None,
- top_k: int = 0,
- tqdm_desc: str = "Recognizing Text"
- ) -> tuple:
- allowed_tasks = self.tasks.keys()
- assert all([task_name in allowed_tasks for task_name in task_names]), (
- f"One or more tasks in {task_names} is not supported. Supported tasks are {allowed_tasks}"
- )
-
- predicted_tokens = [[] for _ in range(len(images))]
- scores = [[] for _ in range(len(images))]
- topk_probs = [[] for _ in range(len(images))]
-
- if batch_size is None:
- batch_size = self.get_batch_size()
-
- batch_size = min(len(images), batch_size)
- current_inputs = None
-
- max_image_tokens = self.get_max_image_token_count(images, task_names)
- if max_sliding_window is None:
- max_sliding_window = self.model.config.sliding_window
- self.setup_cache(
- batch_size,
- max_cache_len=max_image_tokens + max_sliding_window + self.extra_token_count.get(settings.TORCH_DEVICE_MODEL, 0),
- max_sliding_window=max_sliding_window,
- )
-
- batch_max_tokens = {}
- for idx, (img, txt, task) in enumerate(zip(images, input_texts, task_names)):
- self.prompt_queue.append(
- FoundationPrompt(
- id=idx, task_name=task, text=txt, image=img, math_mode=math_mode
- )
- )
- batch_max_tokens[idx] = (
- max_tokens
- or settings.FOUNDATION_MAX_TOKENS
- or self.tasks[task]["max_tokens"]
- )
-
- overall_max_tokens = max(batch_max_tokens.values())
-
- pbar = tqdm(
- total=len(self.prompt_queue),
- desc=tqdm_desc,
- disable=self.disable_tqdm,
- )
-
- batch_bboxes = torch.zeros(len(images), overall_max_tokens, 6)
- batch_pos = [0] * len(images)
-
- while self.prompt_queue or self.num_active_slots > 0:
- if (
- self.num_empty_slots / batch_size
- ) >= self.min_prefill_ratio and self.prompt_queue:
- updated_inputs, outputs, merge_idxs = self.prefill(
- current_inputs, max_lookahead_tokens=0
- )
-
- predicted_tokens_cpu = outputs.preds.cpu()
- scores_cpu = outputs.scores.cpu()
- bbox_preds_cpu = outputs.bbox_preds.cpu()
-
- if top_k > 0:
- batch_top_k_probs, batch_top_k_indices = torch.topk(
- outputs.token_probs, k=top_k, dim=-1
- )
- batch_top_k_probs_cpu = batch_top_k_probs.cpu()
- batch_top_k_indices_cpu = batch_top_k_indices.cpu()
-
- for temp_idx, b_idx in enumerate(merge_idxs):
- if self.batch_prompt_mapping[b_idx] is not None:
- p_idx = self.batch_prompt_mapping[b_idx]
- seq_len = predicted_tokens_cpu.shape[1]
- for t_idx in range(seq_len):
- token = predicted_tokens_cpu[temp_idx, t_idx].item()
- predicted_tokens[p_idx].append(token)
- batch_bboxes[p_idx, batch_pos[p_idx]] = bbox_preds_cpu[
- temp_idx, t_idx
- ]
- batch_pos[p_idx] += 1
- scores[p_idx].append(scores_cpu[temp_idx, t_idx].item())
-
- if top_k > 0:
- top_k_scores = {
- batch_top_k_indices_cpu[temp_idx, t_idx][
- k
- ].item(): batch_top_k_probs_cpu[temp_idx, t_idx][
- k
- ].item()
- for k in range(top_k)
- }
- topk_probs[p_idx].append(top_k_scores)
-
- if token in [
- self.processor.eos_token_id,
- self.processor.no_output_token,
- ]:
- self.batch_prompt_mapping[b_idx] = None
- pbar.update(1)
- break
- else:
- updated_inputs, outputs = self.decode(
- current_inputs, max_lookahead_tokens=max_lookahead_tokens
- )
- mark_step()
-
- predicted_tokens_cpu = outputs.preds.cpu()
- scores_cpu = outputs.scores.cpu()
- bbox_preds_cpu = outputs.bbox_preds.cpu()
-
- if top_k > 0:
- batch_top_k_probs, batch_top_k_indices = torch.topk(
- outputs.token_probs, k=top_k, dim=-1
- )
- batch_top_k_probs_cpu = batch_top_k_probs.cpu()
- batch_top_k_indices_cpu = batch_top_k_indices.cpu()
-
- for b_idx, p_idx in self.batch_prompt_mapping.items():
- if p_idx is not None:
- seq_len = predicted_tokens_cpu.shape[1]
- num_tokens = updated_inputs.num_valid_tokens[b_idx].item()
- should_stop = False
-
- for t_idx in range(seq_len):
- # don't use multitoken prediction for lower confidence tokens
- if t_idx > 0 and num_tokens < seq_len:
- # roll so tokens are right aligned
- updated_inputs.input_ids[b_idx] = (
- updated_inputs.input_ids[b_idx].roll(
- shifts=seq_len - num_tokens, dims=0
- )
- )
- # don't need to roll position_ids because that's handled in `decode` (and when we do beacon tokens)
- break
-
- token = predicted_tokens_cpu[b_idx, t_idx].item()
- predicted_tokens[p_idx].append(token)
- batch_bboxes[p_idx, batch_pos[p_idx]] = bbox_preds_cpu[
- b_idx, t_idx
- ]
- batch_pos[p_idx] += 1
- scores[p_idx].append(scores_cpu[b_idx, t_idx].item())
-
- if top_k > 0:
- top_k_scores = {
- batch_top_k_indices_cpu[temp_idx, t_idx][
- k
- ].item(): batch_top_k_probs_cpu[temp_idx, t_idx][
- k
- ].item()
- for k in range(top_k)
- }
- topk_probs[p_idx].append(top_k_scores)
-
- repeats = len(predicted_tokens[p_idx]) >= batch_max_tokens[
- p_idx
- ] or (
- drop_repeated_tokens
- and detect_repeat_token(predicted_tokens[p_idx])
- and task_names[p_idx]
- in [
- TaskNames.ocr_with_boxes,
- TaskNames.ocr_without_boxes,
- ]
- )
- if (
- token
- in [
- self.processor.eos_token_id,
- self.processor.pad_token_id,
- ]
- or repeats
- ):
- should_stop = True
- break
-
- if should_stop:
- self.batch_prompt_mapping[b_idx] = None
- pbar.update(1)
-
- # Update inputs and mark XLA step
- current_inputs = updated_inputs
-
- pbar.close()
-
- del self.kv_cache
- self.kv_cache = None
- torch.cuda.empty_cache()
-
- return predicted_tokens, batch_bboxes, scores, topk_probs
diff --git a/surya/foundation/cache/__init__.py b/surya/foundation/cache/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/surya/foundation/cache/dynamic_ops.py b/surya/foundation/cache/dynamic_ops.py
deleted file mode 100644
index 168092f3..00000000
--- a/surya/foundation/cache/dynamic_ops.py
+++ /dev/null
@@ -1,399 +0,0 @@
-from typing import Any, Dict, List, Optional, Tuple
-import torch
-from transformers import PretrainedConfig
-
-"""
-Special cache class for the surya foundation model that supports -
-1) Static shape
-2) A custom sliding window, where image tokens stay in cache, and text tokens are popped
-3) Continuous batching - merging etc
-4) Attention mask management - To match with what's currently in the cache
-
-Heavily inspired from https://github.com/huggingface/transformers/blob/0725cd6953803b8aacfc85288cbfb83dea30c469/src/transformers/cache_utils.py#L1079
-"""
-
-
-class DynamicOpsCache:
- def __init__(
- self,
- config: PretrainedConfig,
- batch_size: int,
- max_cache_len: int,
- text_sliding_window: int,
- device: int,
- dtype: int,
- ):
- self.text_sliding_window = text_sliding_window
- self.num_layers = config.num_hidden_layers
- self.max_batch_size = batch_size
- self.max_cache_len = max_cache_len
- self.head_dim = (
- getattr(config, "head_dim", None)
- or config.hidden_size // config.num_attention_heads
- )
- self._dtype = dtype
- self.num_key_value_heads = (
- config.num_attention_heads
- if getattr(config, "num_key_value_heads", None) is None
- else config.num_key_value_heads
- )
-
- # Cache init is taken from huggingface StaticCache - https://github.com/huggingface/transformers/blob/67ddc82fbc7e52c6f42a395b4a6d278c55b77a39/src/transformers/cache_utils.py#L1125
- self.key_cache: list[torch.Tensor] = []
- self.value_cache: list[torch.Tensor] = []
- cache_shape = (
- self.max_batch_size,
- self.num_key_value_heads,
- self.max_cache_len,
- self.head_dim,
- )
- device = torch.device(device) if device is not None else None
- for _ in range(config.num_hidden_layers):
- new_layer_key_cache = torch.zeros(
- cache_shape, dtype=self._dtype, device=device
- )
- new_layer_value_cache = torch.zeros(
- cache_shape, dtype=self._dtype, device=device
- )
- torch._dynamo.mark_static_address(new_layer_key_cache)
- torch._dynamo.mark_static_address(new_layer_value_cache)
- self.key_cache.append(new_layer_key_cache)
- self.value_cache.append(new_layer_value_cache)
-
- self.attention_mask = torch.zeros(
- (self.max_batch_size, self.max_cache_len), device=device, dtype=torch.long
- )
- self.text_token_counts = [
- torch.zeros(self.max_batch_size, dtype=torch.long, device=device)
- for _ in range(self.num_layers)
- ]
-
- self.dtype = dtype
- self.device = device
-
- def update(
- self,
- key_states: torch.Tensor,
- value_states: torch.Tensor,
- layer_idx: int,
- cache_kwargs: Optional[Dict[str, Any]] = None,
- ) -> Tuple[torch.Tensor, torch.Tensor]:
- prefill = cache_kwargs.get("prefill", False)
- update_fn = self._prefill_update if prefill else self._decode_update
- return update_fn(
- self.key_cache[layer_idx],
- self.value_cache[layer_idx],
- key_states,
- value_states,
- self.text_token_counts[layer_idx],
- cache_kwargs,
- )
-
- def update_text_counts(
- self,
- merge_idxs: torch.Tensor,
- valid_batch_size: torch.Tensor,
- new_text_lens: torch.Tensor,
- ):
- new_text_len_tensor = new_text_lens.to(device=self.device)
-
- for layer_idx in range(self.num_layers):
- self.text_token_counts[layer_idx][merge_idxs] = new_text_len_tensor[
- :valid_batch_size
- ]
-
- # Mirrors the logic from _prefill_update
- # Logic is better explained in this funcrtion
- def prefill_attention_mask_update(
- self,
- prefill_attention_mask: torch.Tensor,
- merge_idxs: torch.Tensor,
- valid_batch_mask: torch.Tensor,
- text_lengths: List[int],
- ):
- seq_len = prefill_attention_mask.shape[1]
- sliding_window = self.text_sliding_window
- total_cache_len = self.max_cache_len
- prefix_cache_space = total_cache_len - sliding_window
-
- for batch_idx, cache_idx in enumerate(merge_idxs):
- text_len = text_lengths[batch_idx]
- prefix_len = seq_len - text_len
- self.attention_mask[cache_idx] = 0 # Set default
-
- assert prefix_len > 0, "There are no prefix (image) tokens!"
-
- end_pos = prefix_cache_space
- # Handle prefix part - Which may be left padded
- if prefix_len <= prefix_cache_space:
- start_pos = prefix_cache_space - prefix_len
- self.attention_mask[cache_idx, start_pos:end_pos] = (
- prefill_attention_mask[batch_idx, :prefix_len]
- )
- else:
- self.attention_mask[cache_idx, :end_pos] = prefill_attention_mask[
- batch_idx, prefix_len - prefix_cache_space : prefix_len
- ]
-
- # Handle text part, keeping sliding window in consideration
- # All of the left padding is before the prefix, so we can ignore the prefill_attention_mask here
- if text_len > 0:
- text_cache_start = prefix_cache_space
- if text_len <= sliding_window:
- self.attention_mask[
- cache_idx, text_cache_start : text_cache_start + text_len
- ] = 1
- else:
- self.attention_mask[cache_idx, -sliding_window:] = 1
-
- # Slow impl for now - Prefill time is dominated by the large sequence length forward pass
- def _prefill_update(
- self,
- key_cache: torch.Tensor,
- value_cache: torch.Tensor,
- key_states: torch.Tensor,
- value_states: torch.Tensor,
- text_token_counts: torch.Tensor,
- cache_kwargs: Optional[Dict[str, Any]] = None,
- ):
- cache_idxs: List[int] = cache_kwargs.get("cache_idxs", None)
- text_lengths: List[int] = cache_kwargs.get("text_lengths", None)
- assert cache_idxs is not None, "cache_idxs must be specified during prefill"
- assert text_lengths is not None, "text_lengths must be specified during prefill"
-
- _, _, seq_len, _ = key_states.shape
- total_cache_len = self.max_cache_len
- sliding_window = self.text_sliding_window
- prefix_cache_space = total_cache_len - sliding_window
-
- for batch_idx, cache_idx in enumerate(cache_idxs):
- text_len = text_lengths[batch_idx]
- prefix_len = seq_len - text_len
-
- ###### Handle Image Tokens (Prefix) #####
- # Place image tokens in appropriate cache space, aligned to the **right edge**
- assert prefix_len > 0, "There are no prefix (image) tokens!"
-
- # prefix_len may be greater than the prefix cache space due to left padding - This happens when
- # a different batch element has a large input text during prefill, causing others to have a lot of
- # left padding. We can safely take the last `prefix_cache_space` elements from the kv states, since
- # `prefix_cache_space` is large enough to fit any image, and the rest **has to be** padding
- end_pos = prefix_cache_space
- if prefix_len <= prefix_cache_space:
- start_pos = prefix_cache_space - prefix_len
- key_cache[cache_idx, :, start_pos:end_pos] = key_states[
- batch_idx, :, :prefix_len
- ]
- value_cache[cache_idx, :, start_pos:end_pos] = value_states[
- batch_idx, :, :prefix_len
- ]
- else:
- key_cache[cache_idx, :, :end_pos] = key_states[
- batch_idx, :, prefix_len - prefix_cache_space : prefix_len
- ]
- value_cache[cache_idx, :, :end_pos] = value_states[
- batch_idx, :, prefix_len - prefix_cache_space : prefix_len
- ]
-
- ###### Handle Text Tokens #####
- # Text tokens start at the **left edge** of sliding window cache space
- if text_len > 0:
- text_cache_start = prefix_cache_space
-
- if text_len <= sliding_window:
- key_cache[
- cache_idx, :, text_cache_start : text_cache_start + text_len
- ] = key_states[batch_idx, :, prefix_len : prefix_len + text_len]
- value_cache[
- cache_idx, :, text_cache_start : text_cache_start + text_len
- ] = value_states[batch_idx, :, prefix_len : prefix_len + text_len]
- else:
- start_in_text = text_len - sliding_window
- key_cache[
- cache_idx,
- :,
- text_cache_start : text_cache_start + sliding_window,
- ] = key_states[
- batch_idx, :, prefix_len + start_in_text : prefix_len + text_len
- ]
- value_cache[
- cache_idx,
- :,
- text_cache_start : text_cache_start + sliding_window,
- ] = value_states[
- batch_idx, :, prefix_len + start_in_text : prefix_len + text_len
- ]
-
- # Return the full key/value states (not just cached) for use in subsequent layers
- return key_states, value_states
-
- # """
- # Matches the logic of the decode update, but needs to be called before the updates
- # since some parts of the model depend on the attention mask
- # """
- def decode_attention_mask_update(
- self, num_valid_tokens: torch.Tensor, cache_idxs: List[int]
- ):
- sliding_window = self.text_sliding_window
- text_cache_start = self.max_cache_len - sliding_window
-
- # Using text_token_counts of first layer, should be same for all though
- current_text_lens = self.text_token_counts[0]
- cache_idxs_tensor = torch.tensor(cache_idxs, device=current_text_lens.device)
-
- # Get current text lengths for the relevant cache indices
- current_lens = current_text_lens[cache_idxs_tensor]
- new_text_lens = current_lens + num_valid_tokens
- is_full = new_text_lens > sliding_window
-
- # Handle full caches - set entire sliding window to 1
- if is_full.any():
- full_mask = is_full
- full_cache_idxs = cache_idxs_tensor[full_mask]
- self.attention_mask[full_cache_idxs, text_cache_start:] = 1
-
- # Handle non-full caches - set specific ranges to 1
- if (~is_full).any():
- non_full_mask = ~is_full
- non_full_cache_idxs = cache_idxs_tensor[non_full_mask]
- non_full_current_lens = current_lens[non_full_mask]
- non_full_valid_tokens = num_valid_tokens[non_full_mask]
-
- max_valid_tokens = (
- non_full_valid_tokens.max().item()
- if len(non_full_valid_tokens) > 0
- else 0
- )
- if max_valid_tokens > 0:
- batch_size = len(non_full_cache_idxs)
- offset_range = torch.arange(
- max_valid_tokens, device=current_text_lens.device
- )
- batch_offsets = offset_range.unsqueeze(0).expand(batch_size, -1)
- start_positions = non_full_current_lens.unsqueeze(1)
- valid_token_counts = non_full_valid_tokens.unsqueeze(1)
-
- position_indices = start_positions + batch_offsets
- valid_mask = batch_offsets < valid_token_counts
-
- row_indices = non_full_cache_idxs.unsqueeze(1).expand(
- -1, max_valid_tokens
- )[valid_mask]
- col_indices = text_cache_start + position_indices[valid_mask]
-
- self.attention_mask[row_indices, col_indices] = 1
-
- """
- Static cache update
- - respects per-batch text token limits
- - per-batch valid token lengths (right-padded inputs)
-
- kv states are expected to have shape [batch_size, kv_heads, T_pad, head_dim]
- They may have different `true` lengths, to account for multi token preds, or beacon tokens
- Expects `num_valid_tokens` in cache_kwargs: a tensor of shape (B,) indicating the number
- of actual (non-padded) tokens to add per batch element.
- """
-
- def _decode_update(
- self,
- key_cache: torch.Tensor,
- value_cache: torch.Tensor,
- key_states: torch.Tensor,
- value_states: torch.Tensor,
- text_token_counts: torch.Tensor,
- cache_kwargs: Optional[Dict[str, Any]] = None,
- ) -> Tuple[torch.Tensor, torch.Tensor]:
- num_valid_tokens: torch.Tensor = cache_kwargs.get(
- "num_valid_tokens"
- ) # shape: (B,)
- assert num_valid_tokens is not None, (
- "`num_valid_tokens` must be provided in `cache_kwargs`"
- )
- device = key_states.device
-
- batch_size, num_head, seq_len, head_dim = key_states.shape
- sliding_window = self.text_sliding_window
- max_cache_len = self.max_cache_len
- cache_text_start = max_cache_len - sliding_window
- new_text_lengths = text_token_counts + num_valid_tokens
- slide_amounts = torch.clamp(new_text_lengths - sliding_window, min=0)
- needs_rotate = slide_amounts > 0
-
- # Rotate the cache if needed
- if torch.any(needs_rotate):
- k_slice = key_cache[:, :, -sliding_window:] # shape: [B, H, W, D]
- v_slice = value_cache[:, :, -sliding_window:] # same shape
-
- cache_indices = (
- torch.arange(sliding_window, device=device)
- .unsqueeze(0)
- .repeat(batch_size, 1)
- ) # [B, W]
- rolled_indices = (
- cache_indices + slide_amounts.unsqueeze(1)
- ) % sliding_window # [B, W]
-
- # We need to expand indices to shape: [B, 1, W, 1] to broadcast with k_slice
- rolled_indices = (
- rolled_indices.unsqueeze(1)
- .unsqueeze(-1)
- .expand(-1, num_head, -1, head_dim)
- )
-
- k_slice_rolled = k_slice.gather(dim=2, index=rolled_indices)
- v_slice_rolled = v_slice.gather(dim=2, index=rolled_indices)
-
- key_cache[:, :, -sliding_window:] = k_slice_rolled
- value_cache[:, :, -sliding_window:] = v_slice_rolled
-
- # Insert only **valid tokens** into the cache. These are **right aligned** within the input sequence
- insert_positions = torch.where(
- needs_rotate,
- max_cache_len - num_valid_tokens,
- text_token_counts + cache_text_start,
- )
-
- max_tokens = num_valid_tokens.max().item()
- offsets = torch.arange(max_tokens, device=device).unsqueeze(0) # [1, max_T]
- valid_mask = offsets < num_valid_tokens.unsqueeze(1) # [B, max_T]
- src_indices = (seq_len - num_valid_tokens).unsqueeze(1) + offsets # [B, max_T]
- src_indices = src_indices.clamp(max=seq_len - 1) # safety
-
- tgt_indices = insert_positions.unsqueeze(1) + offsets # [B, max_T]
- tgt_indices = tgt_indices.clamp(max=max_cache_len - 1) # safety
-
- src_idx_exp = (
- src_indices.unsqueeze(1)
- .unsqueeze(-1)
- .expand(batch_size, num_head, max_tokens, head_dim)
- )
- tgt_idx_exp = (
- tgt_indices.unsqueeze(1)
- .unsqueeze(-1)
- .expand(batch_size, num_head, max_tokens, head_dim)
- )
- valid_mask_exp = (
- valid_mask.unsqueeze(1)
- .unsqueeze(-1)
- .expand(batch_size, num_head, max_tokens, head_dim)
- )
-
- k_src = torch.gather(key_states, 2, src_idx_exp)
- v_src = torch.gather(value_states, 2, src_idx_exp)
- k_src = k_src * valid_mask_exp
- v_src = v_src * valid_mask_exp
-
- # Write into cache
- key_cache.scatter_(2, tgt_idx_exp, k_src)
- value_cache.scatter_(2, tgt_idx_exp, v_src)
-
- # In-place edit - Mutates
- text_token_counts += num_valid_tokens
- text_token_counts.clamp_(max=sliding_window)
-
- return key_cache, value_cache
-
- # We have a non-uniform cache, so its better to not return it and handle any logic
- # that requires this ourselves
- def get_seq_length(self, layer_idx: Optional[int] = 0) -> int:
- raise NotImplementedError()
diff --git a/surya/foundation/cache/static_ops.py b/surya/foundation/cache/static_ops.py
deleted file mode 100644
index 0b9ee242..00000000
--- a/surya/foundation/cache/static_ops.py
+++ /dev/null
@@ -1,194 +0,0 @@
-from typing import Any, Dict, List, Optional, Tuple
-import torch
-from transformers import PretrainedConfig
-
-from surya.foundation.cache.dynamic_ops import DynamicOpsCache
-
-"""
-Special cache class for the surya foundation model that supports -
-1) Static shape
-2) A custom sliding window, where image tokens stay in cache, and text tokens are popped
-3) Continuous batching - merging etc
-4) Attention mask management - To match with what's currently in the cache
-
-Heavily inspired from https://github.com/huggingface/transformers/blob/0725cd6953803b8aacfc85288cbfb83dea30c469/src/transformers/cache_utils.py#L1079
-"""
-
-
-class StaticOpsCache(DynamicOpsCache):
- def __init__(
- self,
- config: PretrainedConfig,
- batch_size: int,
- max_cache_len: int,
- text_sliding_window: int,
- device: int,
- dtype: int,
- ):
- self.text_sliding_window = text_sliding_window
- self.num_layers = config.num_hidden_layers
- self.max_batch_size = batch_size
- self.max_cache_len = max_cache_len
- self.head_dim = (
- getattr(config, "head_dim", None)
- or config.hidden_size // config.num_attention_heads
- )
- self._dtype = dtype
- self.num_key_value_heads = (
- config.num_attention_heads
- if getattr(config, "num_key_value_heads", None) is None
- else config.num_key_value_heads
- )
-
- # Cache init is taken from huggingface StaticCache - https://github.com/huggingface/transformers/blob/67ddc82fbc7e52c6f42a395b4a6d278c55b77a39/src/transformers/cache_utils.py#L1125
- self.key_cache: list[torch.Tensor] = []
- self.value_cache: list[torch.Tensor] = []
- cache_shape = (
- self.max_batch_size,
- self.num_key_value_heads,
- self.max_cache_len,
- self.head_dim,
- )
- device = torch.device(device) if device is not None else None
- for _ in range(config.num_hidden_layers):
- new_layer_key_cache = torch.zeros(
- cache_shape, dtype=self._dtype, device=device
- )
- new_layer_value_cache = torch.zeros(
- cache_shape, dtype=self._dtype, device=device
- )
- torch._dynamo.mark_static_address(new_layer_key_cache)
- torch._dynamo.mark_static_address(new_layer_value_cache)
- self.key_cache.append(new_layer_key_cache)
- self.value_cache.append(new_layer_value_cache)
-
- self.attention_mask = torch.zeros(
- (self.max_batch_size, self.max_cache_len), device=device, dtype=torch.long
- )
- self.text_token_counts = [
- torch.zeros(self.max_batch_size, dtype=torch.long, device=device)
- for _ in range(self.num_layers)
- ]
-
- self.dtype = dtype
- self.device = device
-
- def update(
- self,
- key_states: torch.Tensor,
- value_states: torch.Tensor,
- layer_idx: int,
- cache_kwargs: Optional[Dict[str, Any]] = None,
- ) -> Tuple[torch.Tensor, torch.Tensor]:
- prefill = cache_kwargs.get("prefill", False)
- update_fn = self._prefill_update if prefill else self._decode_update
- return update_fn(
- self.key_cache[layer_idx],
- self.value_cache[layer_idx],
- key_states,
- value_states,
- self.text_token_counts[layer_idx],
- cache_kwargs,
- )
-
- def _prefill_update(
- self,
- key_cache: torch.Tensor,
- value_cache: torch.Tensor,
- key_states: torch.Tensor,
- value_states: torch.Tensor,
- text_token_counts: torch.Tensor,
- cache_kwargs: Optional[Dict[str, Any]] = None,
- ):
- cache_idxs: torch.tensor = cache_kwargs.get("cache_idxs", None)
- text_lengths: List[int] = cache_kwargs.get("text_lengths", None)
- assert cache_idxs is not None, "cache_idxs must be specified during prefill"
- assert text_lengths is not None, "text_lengths must be specified during prefill"
-
- cache_idx_length = len(cache_idxs)
- full_batch = len(cache_idxs) == self.max_batch_size
-
- # Insert key and value states at the end of the cache
- new_tokens = key_states.shape[2]
-
- # Direct right-aligned assignment
- if full_batch:
- key_cache[:, :, -new_tokens:] = key_states
- value_cache[:, :, -new_tokens:] = value_states
- else:
- key_cache[cache_idxs, :, -new_tokens:] = key_states[:cache_idx_length]
- value_cache[cache_idxs, :, -new_tokens:] = value_states[:cache_idx_length]
-
- return key_states, value_states
-
- # """
- # Matches the logic of the decode update, but needs to be called before the updates
- # since some parts of the model depend on the attention mask
- # """
- def decode_attention_mask_update(
- self, num_valid_tokens: torch.Tensor, cache_idxs: List[int]
- ):
- max_valid_tokens = num_valid_tokens.max().item()
- if max_valid_tokens == 0:
- # If no valid tokens, we don't need to update the attention mask
- return
-
- # Shift the attention mask to the left by max_valid_tokens
- self.attention_mask = self.attention_mask.roll(-1 * max_valid_tokens, dims=1)
- self.attention_mask[:, -max_valid_tokens:] = (
- 1 # Full attention to all new tokens
- )
-
- # Mirrors the logic from _prefill_update
- def prefill_attention_mask_update(
- self,
- attention_mask: torch.Tensor,
- merge_idxs: torch.Tensor,
- valid_batch_size: torch.Tensor,
- text_lengths: List[int],
- ):
- # Set from -(image_length + text_length) to end to 1 for each batch element
- seq_len = attention_mask.shape[1]
- self.attention_mask[merge_idxs] = (
- 0 # Reset the attention mask for the current batch elements
- )
- self.attention_mask[merge_idxs, -seq_len:] = attention_mask[:valid_batch_size]
-
- def _decode_update(
- self,
- key_cache: torch.Tensor,
- value_cache: torch.Tensor,
- key_states: torch.Tensor,
- value_states: torch.Tensor,
- text_token_counts: torch.Tensor,
- cache_kwargs: Optional[Dict[str, Any]] = None,
- ) -> Tuple[torch.Tensor, torch.Tensor]:
- # Naive, always assumes we'll roll by a fixed amount
- # Needs left padding with beacons to work properly
-
- num_valid_tokens: torch.Tensor = cache_kwargs.get(
- "num_valid_tokens"
- ) # shape: (B,)
- assert num_valid_tokens is not None, (
- "`num_valid_tokens` must be provided in `cache_kwargs`"
- )
- # (B, H, L, D)
-
- valid_tokens = key_states.shape[2]
-
- key_cache.copy_(torch.roll(key_cache, -valid_tokens, dims=2))
- value_cache.copy_(torch.roll(value_cache, -valid_tokens, dims=2))
-
- key_cache[:, :, -valid_tokens:, :] = key_states
- value_cache[:, :, -valid_tokens:, :] = value_states
-
- # In-place edit - Mutates
- text_token_counts += num_valid_tokens
- text_token_counts.clamp_(max=self.text_sliding_window)
- return key_cache, value_cache
-
- # The attention mask managed by our kv cache automatically masks the tokens
- # in the cache, so we can return full length for HF to use in other places
- # This is mainly utilized in the cache_positions creation
- def get_seq_length(self, layer_idx: Optional[int] = 0) -> int:
- return self.max_cache_len
diff --git a/surya/foundation/loader.py b/surya/foundation/loader.py
deleted file mode 100644
index 77e6256d..00000000
--- a/surya/foundation/loader.py
+++ /dev/null
@@ -1,115 +0,0 @@
-from typing import Optional
-
-import torch
-from transformers.utils import is_flash_attn_2_available
-
-from surya.common.load import ModelLoader
-from surya.common.surya.config import SuryaModelConfig
-from surya.common.surya import SuryaModel, SuryaXLAModel
-from surya.common.surya.processor import SuryaOCRProcessor
-from surya.common.surya.processor.tokenizer import SuryaOCRTokenizer
-from surya.common.util import is_flash_attn_2_supported
-from surya.common.xla import get_compile_args
-from surya.logging import get_logger
-from surya.settings import settings
-
-logger = get_logger()
-
-
-class FoundationModelLoader(ModelLoader):
- def __init__(self, checkpoint: Optional[str] = None):
- super().__init__(checkpoint)
-
- if self.checkpoint is None:
- self.checkpoint = settings.FOUNDATION_MODEL_CHECKPOINT
-
- def model(
- self,
- device=settings.TORCH_DEVICE_MODEL,
- dtype=None,
- attention_implementation: Optional[str] = None,
- ) -> SuryaModel:
- if device is None:
- device = settings.TORCH_DEVICE_MODEL
- if dtype is None:
- # See https://github.com/pytorch/pytorch/issues/118122 - T4 (device version 7.5) will return true since it supports
- # emulated bf16, but falls back to very slow kernels, especially for SDPA
- dtype = settings.MODEL_DTYPE_BFLOAT
- if device == "cuda" and not torch.cuda.is_bf16_supported(
- including_emulation=False
- ):
- # If the device is cuda, we check if bf16 is supported, and if not, we use float16
- dtype = settings.MODEL_DTYPE
- elif dtype == torch.float16:
- dtype = torch.bfloat16 # Model weights in bfloat16
-
- config = SuryaModelConfig.from_pretrained(self.checkpoint)
-
- if attention_implementation is not None:
- config.decoder._attn_implementation = attention_implementation
- config.vision_encoder._attn_implementation = attention_implementation
- elif is_flash_attn_2_available() and is_flash_attn_2_supported(device):
- config.decoder._attn_implementation = "flash_attention_2"
- config.vision_encoder._attn_implementation = "flash_attention_2"
- elif device == "xla":
- config.decoder._attn_implementation = "sdpa"
- config.vision_encoder._attn_implementation = "sdpa"
- else:
- config.decoder._attn_implementation = "sdpa"
- config.vision_encoder._attn_implementation = "sdpa"
-
- model_cls = SuryaModel
- if device == "xla":
- model_cls = SuryaXLAModel
-
- config._attn_implementation_autoset = True
- config.vision_encoder._attn_implementation_autoset = True
- config.decoder._attn_implementation_autoset = True
-
- model = model_cls.from_pretrained(
- self.checkpoint, dtype=dtype, config=config, ignore_mismatched_sizes=True
- ).to(device)
- model = model.eval()
-
- if settings.COMPILE_ALL or settings.COMPILE_FOUNDATION:
- torch._dynamo.config.cache_size_limit = 1000
- torch._dynamo.config.suppress_errors = True
- torch._dynamo.config.specialize_int = False
- torch._dynamo.config.allow_unspec_int_on_nn_module = True
- torch._dynamo.config.capture_scalar_outputs = True
- torch._dynamo.config.recompile_limit = 32
-
- logger.info(
- f"Compiling foundation model {self.checkpoint} on device {device} with dtype {dtype}"
- )
- compile_args = get_compile_args(device)
- model.vision_encoder = torch.compile(model.vision_encoder, **compile_args)
- model.decoder = torch.compile(model.decoder, **compile_args)
-
- logger.debug(
- f"Loaded recognition model {self.checkpoint} from {SuryaModel.get_local_path(self.checkpoint)} onto device {model.device} with dtype {dtype}, using decoder attention mechanism {model.config.decoder._attn_implementation}, encoder attention mechanism {model.config.vision_encoder._attn_implementation}."
- )
- return model
-
- def processor(
- self, device=settings.TORCH_DEVICE_MODEL, dtype=settings.MODEL_DTYPE_BFLOAT
- ) -> SuryaOCRProcessor:
- config: SuryaModelConfig = SuryaModelConfig.from_pretrained(self.checkpoint)
-
- ocr_tokenizer = SuryaOCRTokenizer(
- special_tokens=config.special_ocr_tokens, model_checkpoint=self.checkpoint
- )
-
- processor = SuryaOCRProcessor(
- ocr_tokenizer=ocr_tokenizer,
- blank_bbox_token_id=config.blank_bbox_token_id,
- num_register_tokens=config.num_register_tokens,
- sequence_length=None,
- patch_size=config.vision_encoder.patch_size,
- merge_size=config.vision_encoder.spatial_merge_size,
- model_device=device,
- num_beacon_tokens=config.num_beacon_tokens,
- beacon_token_interval=config.beacon_token_interval,
- )
-
- return processor
diff --git a/surya/foundation/util.py b/surya/foundation/util.py
deleted file mode 100644
index 63e92fe1..00000000
--- a/surya/foundation/util.py
+++ /dev/null
@@ -1,66 +0,0 @@
-from typing import List, Tuple
-import numpy as np
-import torch
-
-def detect_repeat_token(predicted_tokens: List[int], max_repeats: int = 40):
- if len(predicted_tokens) < max_repeats:
- return False
-
- # Detect repeats containing 1 or 2 tokens
- last_n = predicted_tokens[-max_repeats:]
- unique_tokens = len(set(last_n))
- if unique_tokens > 5:
- return False
-
- return last_n[-unique_tokens:] == last_n[-unique_tokens * 2 : -unique_tokens]
-
-def prediction_to_polygon_batch(
- pred: torch.Tensor,
- img_sizes: List[Tuple[int, int]],
- bbox_scaler,
- skew_scaler,
- skew_min=0.001,
-):
- img_sizes = torch.from_numpy(np.array(img_sizes, dtype=np.float32)).to(
- pred.device
- )
- w_scale = (img_sizes[:, 1] / bbox_scaler)[:, None, None]
- h_scale = (img_sizes[:, 0] / bbox_scaler)[:, None, None]
-
- cx = pred[:, :, 0]
- cy = pred[:, :, 1]
- width = pred[:, :, 2]
- height = pred[:, :, 3]
-
- x1 = cx - width / 2
- y1 = cy - height / 2
- x2 = cx + width / 2
- y2 = cy + height / 2
-
- skew_x = torch.floor((pred[:, :, 4] - skew_scaler) / 2)
- skew_y = torch.floor((pred[:, :, 5] - skew_scaler) / 2)
-
- skew_x[torch.abs(skew_x) < skew_min] = 0
- skew_y[torch.abs(skew_y) < skew_min] = 0
-
- polygons_flat = torch.stack(
- [
- x1 - skew_x,
- y1 - skew_y,
- x2 - skew_x,
- y1 + skew_y,
- x2 + skew_x,
- y2 + skew_y,
- x1 + skew_x,
- y2 - skew_y,
- ],
- dim=2,
- )
-
- batch_size, seq_len, _ = pred.shape
- polygons = polygons_flat.view(batch_size, seq_len, 4, 2)
-
- polygons[:, :, :, 0] *= w_scale
- polygons[:, :, :, 1] *= h_scale
-
- return polygons
\ No newline at end of file
diff --git a/surya/inference/__init__.py b/surya/inference/__init__.py
new file mode 100644
index 00000000..be1faeb4
--- /dev/null
+++ b/surya/inference/__init__.py
@@ -0,0 +1,115 @@
+"""Surya inference manager.
+
+One process owns one SuryaInferenceManager. The manager wraps a single backend
+(vllm | llamacpp) which speaks OpenAI-compatible chat completions.
+
+Predictors take the manager via explicit injection at construction time.
+"""
+
+from __future__ import annotations
+
+import os
+import shutil
+import subprocess
+from typing import List, Optional
+
+from surya.inference.backends.base import Backend
+from surya.inference.schema import BatchInputItem, BatchOutputItem
+from surya.logging import get_logger
+from surya.settings import settings
+
+logger = get_logger()
+
+
+def _has_nvidia_gpu() -> bool:
+ """True if an NVIDIA GPU is present on this host.
+
+ We deliberately do *not* rely solely on ``torch.cuda.is_available()``:
+ the installed torch wheel's CUDA build can be newer than the host driver
+ (PyPI's default wheel tracks the latest CUDA), in which case torch reports
+ no CUDA even on a perfectly good GPU box. That would silently route us to
+ the CPU llama.cpp backend on a machine that should be running vllm. So we
+ take torch's word when it *does* see CUDA, and otherwise fall back to
+ probing for the GPU directly via ``nvidia-smi``.
+ """
+ try:
+ import torch
+
+ if torch.cuda.is_available():
+ return True
+ except Exception:
+ pass
+
+ # Instant, load-independent check: the NVIDIA device node only exists when
+ # a GPU + driver are present. Preferred over nvidia-smi because nvidia-smi
+ # can block for several seconds on a GPU under heavy load, which would race
+ # a timeout and falsely report "no GPU".
+ if os.path.exists("/dev/nvidia0"):
+ return True
+
+ nvidia_smi = shutil.which("nvidia-smi")
+ if not nvidia_smi:
+ return False
+ try:
+ result = subprocess.run(
+ [nvidia_smi, "-L"], capture_output=True, text=True, timeout=15
+ )
+ return result.returncode == 0 and "GPU" in result.stdout
+ except Exception:
+ return False
+
+
+def _autodetect_backend() -> str:
+ if settings.SURYA_INFERENCE_BACKEND:
+ return settings.SURYA_INFERENCE_BACKEND
+ # NVIDIA GPU → vllm, mps/cpu → llamacpp
+ if _has_nvidia_gpu():
+ return "vllm"
+ return "llamacpp"
+
+
+def _build_backend(method: str) -> Backend:
+ method = method.lower()
+ if method == "vllm":
+ from surya.inference.backends.vllm import VllmBackend
+
+ return VllmBackend()
+ if method == "llamacpp":
+ from surya.inference.backends.llamacpp import LlamaCppBackend
+
+ return LlamaCppBackend()
+ raise ValueError(
+ f"Unknown inference backend {method!r}. Supported: 'vllm', 'llamacpp'."
+ )
+
+
+class SuryaInferenceManager:
+ """Single entry point for VLM inference. Construct once per process."""
+
+ def __init__(self, method: Optional[str] = None, lazy: bool = True):
+ self.method = method or _autodetect_backend()
+ self.backend: Backend = _build_backend(self.method)
+ if not lazy:
+ self.backend.start()
+
+ def start(self) -> None:
+ self.backend.start()
+
+ def stop(self) -> None:
+ self.backend.stop()
+
+ def generate(self, batch: List[BatchInputItem]) -> List[BatchOutputItem]:
+ return self.backend.generate(batch)
+
+
+# Module-level lazy singleton for callers that don't want explicit construction
+# (notebooks, ad-hoc scripts). Surya's own models.py and marker should use
+# explicit construction.
+_default_manager: Optional[SuryaInferenceManager] = None
+
+
+def get_default_manager() -> SuryaInferenceManager:
+ global _default_manager
+ if _default_manager is None:
+ _default_manager = SuryaInferenceManager()
+ return _default_manager
diff --git a/surya/inference/backends/__init__.py b/surya/inference/backends/__init__.py
new file mode 100644
index 00000000..f81cf027
--- /dev/null
+++ b/surya/inference/backends/__init__.py
@@ -0,0 +1,2 @@
+from surya.inference.backends.base import Backend as Backend
+from surya.inference.backends.base import ServerHandle as ServerHandle
diff --git a/surya/inference/backends/base.py b/surya/inference/backends/base.py
new file mode 100644
index 00000000..d97dad27
--- /dev/null
+++ b/surya/inference/backends/base.py
@@ -0,0 +1,31 @@
+from __future__ import annotations
+
+from dataclasses import dataclass
+from typing import List
+
+
+from surya.inference.schema import BatchInputItem, BatchOutputItem
+
+
+@dataclass
+class ServerHandle:
+ base_url: str # e.g. "http://127.0.0.1:8765/v1"
+ model_name: str # what gets passed in OpenAI `model` field
+ spawned_by_us: bool # if True, we manage atexit cleanup
+
+
+class Backend:
+ """Abstract backend. Concrete backends own server lifecycle + generation."""
+
+ name: str # "vllm" | "llamacpp"
+
+ def start(self) -> ServerHandle:
+ """Idempotent: probe → attach if alive, else spawn. Returns handle."""
+ raise NotImplementedError
+
+ def stop(self) -> None:
+ """Stop the server if we spawned it."""
+ raise NotImplementedError
+
+ def generate(self, batch: List[BatchInputItem]) -> List[BatchOutputItem]:
+ raise NotImplementedError
diff --git a/surya/inference/backends/llamacpp.py b/surya/inference/backends/llamacpp.py
new file mode 100644
index 00000000..c97c6fe5
--- /dev/null
+++ b/surya/inference/backends/llamacpp.py
@@ -0,0 +1,207 @@
+"""llama.cpp backend: spawns the upstream `llama-server` binary natively.
+
+Install:
+- macOS: brew install llama.cpp (Metal build, MPS)
+- Linux: brew install llama.cpp OR github.com/ggml-org/llama.cpp/releases
+"""
+
+from __future__ import annotations
+
+import os
+import shutil
+import subprocess
+from pathlib import Path
+from typing import List, Optional
+
+from huggingface_hub import hf_hub_download
+from openai import OpenAI
+
+from surya.inference.backends.base import Backend, ServerHandle
+from surya.inference.backends.openai_client import chat_completions_batch
+from surya.inference.backends.spawn import (
+ SpawnHandle,
+ SpawnError,
+ attach_or_spawn,
+)
+from surya.inference.schema import BatchInputItem, BatchOutputItem
+from surya.logging import get_logger
+from surya.settings import settings
+
+logger = get_logger()
+
+
+def _resolve_llama_server_binary() -> str:
+ binary = settings.LLAMA_CPP_BINARY
+ if binary and os.path.isfile(binary):
+ return binary
+ found = shutil.which(binary or "llama-server")
+ if found:
+ return found
+ raise SpawnError(
+ "llama-server binary not found. Install with:\n"
+ " macOS: brew install llama.cpp\n"
+ " Linux: brew install llama.cpp OR download from\n"
+ " https://github.com/ggml-org/llama.cpp/releases\n"
+ "Or set LLAMA_CPP_BINARY in your env to the binary path."
+ )
+
+
+def _download_gguf_files() -> tuple[str, str]:
+ """Download model + mmproj GGUFs from HF Hub. Returns local paths."""
+ repo = settings.SURYA_GGUF_REPO
+ model_file = settings.SURYA_GGUF_MODEL_FILE
+ mmproj_file = settings.SURYA_GGUF_MMPROJ_FILE
+ logger.info(f"Downloading {model_file} and {mmproj_file} from {repo}")
+ model_path = hf_hub_download(repo_id=repo, filename=model_file)
+ mmproj_path = hf_hub_download(repo_id=repo, filename=mmproj_file)
+ return model_path, mmproj_path
+
+
+def _health_url(port: int) -> str:
+ return f"http://{settings.SURYA_INFERENCE_HOST}:{port}"
+
+
+def _openai_url(port: int) -> str:
+ return f"http://{settings.SURYA_INFERENCE_HOST}:{port}/v1"
+
+
+class LlamaCppBackend(Backend):
+ name = "llamacpp"
+
+ def __init__(self):
+ self.handle: Optional[ServerHandle] = None
+ self._client: Optional[OpenAI] = None
+
+ def start(self) -> ServerHandle:
+ if self.handle is not None:
+ return self.handle
+
+ # If user pinned an external server, attach without spawning.
+ # No binary or GGUF download needed in that case.
+ if settings.SURYA_INFERENCE_URL:
+ spawned = attach_or_spawn(
+ backend=self.name,
+ expected_model_name=settings.SURYA_MODEL_CHECKPOINT,
+ spawn_fn=lambda port: SpawnHandle(
+ pid=None, cleanup_id="", cleanup_kind="process"
+ ), # never called
+ health_url_for=_health_url,
+ openai_url_for=_openai_url,
+ startup_timeout=settings.SURYA_INFERENCE_STARTUP_TIMEOUT,
+ )
+ self.handle = ServerHandle(
+ base_url=spawned.base_url,
+ model_name=spawned.model_name,
+ spawned_by_us=spawned.spawned_by_us,
+ )
+ self._client = OpenAI(api_key="EMPTY", base_url=self.handle.base_url)
+ return self.handle
+
+ binary = _resolve_llama_server_binary()
+
+ # Pre-download GGUFs so the spawn doesn't race the download
+ if (
+ settings.SURYA_GGUF_LOCAL_MODEL_PATH
+ and settings.SURYA_GGUF_LOCAL_MMPROJ_PATH
+ ):
+ model_path = settings.SURYA_GGUF_LOCAL_MODEL_PATH
+ mmproj_path = settings.SURYA_GGUF_LOCAL_MMPROJ_PATH
+ else:
+ model_path, mmproj_path = _download_gguf_files()
+
+ # Total KV-cache budget. llama-server divides --ctx-size across
+ # --parallel slots, so a too-small total silently truncates outputs
+ # once each slot's share fills. Scale with parallel by default;
+ # SURYA_INFERENCE_CTX_SIZE overrides to a fixed value if set.
+ parallel = settings.SURYA_INFERENCE_PARALLEL
+ per_slot = settings.SURYA_INFERENCE_CTX_PER_SLOT
+ ctx_size = settings.SURYA_INFERENCE_CTX_SIZE
+ if ctx_size is None:
+ ctx_size = max(16384, parallel * per_slot)
+ effective_per_slot = ctx_size // max(parallel, 1)
+ logger.info(
+ f"llama-server ctx-size={ctx_size} "
+ f"(~{effective_per_slot}/slot × {parallel} parallel slots)"
+ )
+ if effective_per_slot < per_slot:
+ logger.warning(
+ f"per-slot ctx ({effective_per_slot}) is below recommended "
+ f"{per_slot}; outputs may truncate. Raise "
+ f"SURYA_INFERENCE_CTX_SIZE or SURYA_INFERENCE_CTX_PER_SLOT, "
+ f"or lower SURYA_INFERENCE_PARALLEL."
+ )
+
+ def spawn_fn(port: int) -> SpawnHandle:
+ cmd = [
+ binary,
+ "-m",
+ model_path,
+ "--mmproj",
+ mmproj_path,
+ "-ngl",
+ str(settings.LLAMA_CPP_NGL),
+ "--host",
+ settings.SURYA_INFERENCE_HOST,
+ "--port",
+ str(port),
+ "--parallel",
+ str(parallel),
+ "--ctx-size",
+ str(ctx_size),
+ "--no-mmproj-offload" if settings.LLAMA_CPP_NO_MMPROJ_OFFLOAD else "",
+ "--alias",
+ settings.SURYA_MODEL_CHECKPOINT,
+ "--jinja",
+ ]
+ cmd = [c for c in cmd if c]
+ for extra in (settings.LLAMA_CPP_EXTRA_ARGS or "").split():
+ cmd.append(extra)
+ logger.info(f"Spawning: {' '.join(cmd)}")
+ log_path = Path("~/.cache/datalab/surya/llamacpp_server.log").expanduser()
+ log_path.parent.mkdir(parents=True, exist_ok=True)
+ log_fp = open(log_path, "ab")
+ proc = subprocess.Popen(
+ cmd,
+ stdout=log_fp,
+ stderr=subprocess.STDOUT,
+ start_new_session=True,
+ )
+ return SpawnHandle(
+ pid=proc.pid, cleanup_id=str(proc.pid), cleanup_kind="process"
+ )
+
+ spawned = attach_or_spawn(
+ backend=self.name,
+ expected_model_name=settings.SURYA_MODEL_CHECKPOINT,
+ spawn_fn=spawn_fn,
+ health_url_for=_health_url,
+ openai_url_for=_openai_url,
+ startup_timeout=settings.SURYA_INFERENCE_STARTUP_TIMEOUT,
+ )
+ self.handle = ServerHandle(
+ base_url=spawned.base_url,
+ model_name=spawned.model_name,
+ spawned_by_us=spawned.spawned_by_us,
+ )
+ self._client = OpenAI(
+ api_key="EMPTY",
+ base_url=self.handle.base_url,
+ )
+ return self.handle
+
+ def stop(self) -> None:
+ # atexit handler in spawn.py owns cleanup; nothing to do here.
+ self.handle = None
+ self._client = None
+
+ def generate(self, batch: List[BatchInputItem]) -> List[BatchOutputItem]:
+ if self.handle is None or self._client is None:
+ self.start()
+ return chat_completions_batch(
+ batch,
+ client=self._client,
+ model_name=self.handle.model_name,
+ timeout=settings.SURYA_INFERENCE_TIMEOUT_SECONDS,
+ max_workers=settings.SURYA_INFERENCE_PARALLEL,
+ request_logprobs_default=settings.SURYA_INFERENCE_LOGPROBS,
+ )
diff --git a/surya/inference/backends/openai_client.py b/surya/inference/backends/openai_client.py
new file mode 100644
index 00000000..2d08e65d
--- /dev/null
+++ b/surya/inference/backends/openai_client.py
@@ -0,0 +1,213 @@
+"""Shared OpenAI-compatible chat completions client. Used by vllm + llama.cpp.
+
+Both servers expose `/v1/chat/completions` with the same request/response shape,
+so this module is the single point of HTTP contact for both backends.
+"""
+
+from __future__ import annotations
+
+import base64
+import io
+import math
+import time
+from concurrent.futures import ThreadPoolExecutor
+from typing import List, Optional
+
+from PIL import Image
+
+from surya.inference.prompts import PROMPT_MAPPING
+from surya.inference.schema import (
+ BatchInputItem,
+ BatchOutputItem,
+ GenerationResult,
+)
+from surya.inference.util import detect_repeat_token, scale_to_fit
+from surya.logging import get_logger
+
+logger = get_logger()
+
+
+def encode_image_b64(image: Image.Image) -> str:
+ buf = io.BytesIO()
+ image.save(buf, format="PNG")
+ return base64.b64encode(buf.getvalue()).decode("ascii")
+
+
+def _build_messages(image: Image.Image, prompt: str):
+ image_b64 = encode_image_b64(image)
+ return [
+ {
+ "role": "user",
+ "content": [
+ {
+ "type": "image_url",
+ "image_url": {"url": f"data:image/png;base64,{image_b64}"},
+ },
+ {"type": "text", "text": prompt},
+ ],
+ }
+ ]
+
+
+def _mean_token_prob(logprobs_content) -> Optional[float]:
+ if not logprobs_content:
+ return None
+ probs = []
+ for tok in logprobs_content:
+ lp = (
+ tok.get("logprob")
+ if isinstance(tok, dict)
+ else getattr(tok, "logprob", None)
+ )
+ if lp is None:
+ continue
+ probs.append(math.exp(lp))
+ if not probs:
+ return None
+ return sum(probs) / len(probs)
+
+
+def _generate_one(
+ item: BatchInputItem,
+ client,
+ model_name: str,
+ max_tokens_default: int,
+ temperature: float,
+ top_p: float,
+ timeout: float,
+ request_logprobs_default: bool,
+) -> GenerationResult:
+ prompt = item.prompt or PROMPT_MAPPING[item.prompt_type]
+ image = scale_to_fit(item.image)
+ messages = _build_messages(image, prompt)
+
+ max_tokens = item.max_tokens or max_tokens_default
+ request_logprobs = item.request_logprobs or request_logprobs_default
+
+ kwargs = dict(
+ model=model_name,
+ messages=messages,
+ max_tokens=max_tokens,
+ temperature=temperature,
+ top_p=top_p,
+ timeout=timeout,
+ )
+ if request_logprobs:
+ kwargs["logprobs"] = True
+
+ # Structured output: prefer OpenAI-standard response_format (works on both
+ # vllm and llama.cpp). Fall back to vllm's extra_body for guided_regex.
+ if item.guided_json is not None:
+ kwargs["response_format"] = {
+ "type": "json_schema",
+ "json_schema": {
+ "name": "structured_output",
+ "schema": item.guided_json,
+ "strict": True,
+ },
+ }
+ if item.guided_regex is not None:
+ kwargs.setdefault("extra_body", {})["guided_regex"] = item.guided_regex
+
+ try:
+ completion = client.chat.completions.create(**kwargs)
+ raw = completion.choices[0].message.content or ""
+ token_count = completion.usage.completion_tokens if completion.usage else 0
+ mean_p = None
+ logprobs_content = None
+ if request_logprobs:
+ choice = completion.choices[0]
+ lp = getattr(choice, "logprobs", None)
+ if lp is not None:
+ content = getattr(lp, "content", None)
+ if content is not None:
+ logprobs_content = [
+ c.model_dump() if hasattr(c, "model_dump") else c
+ for c in content
+ ]
+ mean_p = _mean_token_prob(content)
+ return GenerationResult(
+ raw=raw,
+ token_count=token_count,
+ error=False,
+ mean_token_prob=mean_p,
+ logprobs=logprobs_content,
+ )
+ except Exception as e:
+ logger.warning(f"Inference error: {e}")
+ return GenerationResult(raw="", token_count=0, error=True)
+
+
+def _should_retry(
+ result: GenerationResult,
+ retries: int,
+ max_retries: int,
+) -> bool:
+ if retries >= max_retries:
+ return False
+ if result.error:
+ return True
+ has_repeat = detect_repeat_token(result.raw) or (
+ len(result.raw) > 50 and detect_repeat_token(result.raw, cut_from_end=50)
+ )
+ return has_repeat
+
+
+def chat_completions_batch(
+ batch: List[BatchInputItem],
+ client,
+ model_name: str,
+ max_tokens_default: int = 2048,
+ temperature: float = 0.0,
+ top_p: float = 0.1,
+ timeout: float = 600.0,
+ max_workers: Optional[int] = None,
+ max_retries: int = 3,
+ request_logprobs_default: bool = True,
+) -> List[BatchOutputItem]:
+ """Run a batch of items through the chat completions endpoint with concurrent workers."""
+ if not batch:
+ return []
+ if max_workers is None:
+ max_workers = min(64, len(batch))
+
+ def _process(item: BatchInputItem) -> BatchOutputItem:
+ result = _generate_one(
+ item,
+ client=client,
+ model_name=model_name,
+ max_tokens_default=max_tokens_default,
+ temperature=temperature,
+ top_p=top_p,
+ timeout=timeout,
+ request_logprobs_default=request_logprobs_default,
+ )
+ retries = 0
+ while _should_retry(result, retries, max_retries):
+ backoff = 1.5 * (retries + 1) if result.error else 0
+ if backoff:
+ time.sleep(backoff)
+ retry_temp = min(temperature + 0.2 * (retries + 1), 0.8)
+ retry_top_p = 0.95 if not result.error else top_p
+ result = _generate_one(
+ item,
+ client=client,
+ model_name=model_name,
+ max_tokens_default=max_tokens_default,
+ temperature=retry_temp,
+ top_p=retry_top_p,
+ timeout=timeout,
+ request_logprobs_default=request_logprobs_default,
+ )
+ retries += 1
+ return BatchOutputItem(
+ raw=result.raw,
+ token_count=result.token_count,
+ error=result.error,
+ mean_token_prob=result.mean_token_prob,
+ logprobs=result.logprobs,
+ metadata=item.metadata,
+ )
+
+ with ThreadPoolExecutor(max_workers=max_workers) as executor:
+ return list(executor.map(_process, batch))
diff --git a/surya/inference/backends/spawn.py b/surya/inference/backends/spawn.py
new file mode 100644
index 00000000..e1d6ff1b
--- /dev/null
+++ b/surya/inference/backends/spawn.py
@@ -0,0 +1,351 @@
+"""Server lifecycle: probe, filelock, sentinel, atexit cleanup.
+
+Pattern: probe `/health` → if alive return handle → else acquire lock, re-probe,
+spawn detached, write sentinel, register atexit kill (only the spawner cleans up).
+"""
+
+from __future__ import annotations
+
+import atexit
+import json
+import os
+import socket
+import subprocess
+import time
+from dataclasses import dataclass
+from pathlib import Path
+from typing import Callable, Optional
+
+import httpx
+
+from surya.logging import get_logger
+from surya.settings import settings
+
+logger = get_logger()
+
+
+def _cache_dir() -> Path:
+ base = Path(os.path.expanduser("~/.cache/datalab/surya"))
+ base.mkdir(parents=True, exist_ok=True)
+ return base
+
+
+def _sentinel_path(backend: str) -> Path:
+ return _cache_dir() / f"{backend}_server.json"
+
+
+def _lock_path(backend: str) -> Path:
+ return _cache_dir() / f"{backend}_server.lock"
+
+
+def find_free_port() -> int:
+ with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
+ s.bind(("127.0.0.1", 0))
+ return s.getsockname()[1]
+
+
+def probe_health(base_url: str, timeout: float = 1.0) -> bool:
+ """Returns True if the server reports healthy at /health."""
+ try:
+ # llama.cpp returns 200 on /health when ready; vllm returns 200 on /health too.
+ with httpx.Client(timeout=timeout) as client:
+ r = client.get(f"{base_url}/health")
+ return r.status_code == 200
+ except Exception:
+ return False
+
+
+def wait_for_health(
+ base_url: str, total_timeout: float = 300.0, interval: float = 1.0
+) -> bool:
+ deadline = time.time() + total_timeout
+ while time.time() < deadline:
+ if probe_health(base_url):
+ return True
+ time.sleep(interval)
+ return False
+
+
+def probe_model_id(openai_base: str, timeout: float = 5.0) -> Optional[str]:
+ """Returns the model id reported by the running server, or None on failure."""
+ try:
+ with httpx.Client(timeout=timeout) as client:
+ r = client.get(f"{openai_base}/models")
+ r.raise_for_status()
+ data = r.json()
+ models = data.get("data") or []
+ if models:
+ return models[0].get("id")
+ except Exception:
+ return None
+ return None
+
+
+@dataclass
+class SpawnedServer:
+ base_url: str # full openai base, e.g. "http://127.0.0.1:8765/v1"
+ health_url: str # base for /health, e.g. "http://127.0.0.1:8765"
+ model_name: str # what to pass as `model`
+ pid: Optional[int]
+ backend: str
+ spawned_by_us: bool
+
+
+class SpawnError(RuntimeError):
+ pass
+
+
+def _read_sentinel(backend: str) -> Optional[dict]:
+ p = _sentinel_path(backend)
+ if not p.exists():
+ return None
+ try:
+ return json.loads(p.read_text())
+ except Exception:
+ return None
+
+
+def _write_sentinel(backend: str, data: dict) -> None:
+ _sentinel_path(backend).write_text(json.dumps(data))
+
+
+def _delete_sentinel(backend: str) -> None:
+ p = _sentinel_path(backend)
+ if p.exists():
+ try:
+ p.unlink()
+ except Exception:
+ pass
+
+
+def _stop_process(pid: int, name: str) -> None:
+ try:
+ # Graceful first
+ os.kill(pid, 15) # SIGTERM
+ for _ in range(20):
+ try:
+ os.kill(pid, 0) # still alive?
+ except ProcessLookupError:
+ logger.info(f"Stopped {name} (pid {pid})")
+ return
+ time.sleep(0.5)
+ # Hard
+ os.kill(pid, 9)
+ logger.warning(f"Force-killed {name} (pid {pid})")
+ except ProcessLookupError:
+ pass
+ except Exception as e:
+ logger.warning(f"Failed to stop {name} (pid {pid}): {e}")
+
+
+def _capture_server_logs(handle: "SpawnHandle", tail: int = 100) -> str:
+ """Best-effort tail of a server's logs, for surfacing startup failures."""
+ try:
+ if handle.cleanup_kind == "docker":
+ r = subprocess.run(
+ ["docker", "logs", "--tail", str(tail), handle.cleanup_id],
+ capture_output=True,
+ text=True,
+ timeout=15,
+ )
+ return (r.stdout or "") + (r.stderr or "") or "(no docker logs)"
+ # llama.cpp process backend logs to this file (see llamacpp.py)
+ log_path = Path("~/.cache/datalab/surya/llamacpp_server.log").expanduser()
+ if log_path.exists():
+ lines = log_path.read_text(errors="replace").splitlines()
+ return "\n".join(lines[-tail:]) or "(empty log)"
+ except Exception as e:
+ return f"(could not capture logs: {e})"
+ return "(no logs available)"
+
+
+def _stop_docker_container(name: str) -> None:
+ try:
+ subprocess.run(
+ ["docker", "stop", name], check=False, capture_output=True, timeout=30
+ )
+ logger.info(f"Stopped docker container {name}")
+ except Exception as e:
+ logger.warning(f"Failed to stop docker container {name}: {e}")
+
+
+def attach_or_spawn(
+ backend: str,
+ expected_model_name: str,
+ spawn_fn: Callable[[int], "SpawnHandle"],
+ health_url_for: Callable[[int], str],
+ openai_url_for: Callable[[int], str],
+ startup_timeout: float = 600.0,
+) -> SpawnedServer:
+ """Generic attach-or-spawn with file lock and sentinel.
+
+ `spawn_fn(port)` must launch the server detached and return a SpawnHandle
+ with `pid` (int or None for docker) and a `cleanup_id` (e.g. container name).
+ """
+ # 0. If user pinned an external URL, attach without lock
+ if settings.SURYA_INFERENCE_URL:
+ base_url = settings.SURYA_INFERENCE_URL.rstrip("/")
+ health_url = base_url[: -len("/v1")] if base_url.endswith("/v1") else base_url
+ if not probe_health(health_url):
+ raise SpawnError(
+ f"SURYA_INFERENCE_URL={base_url} is not reachable at /health. "
+ "Start the server or unset the variable."
+ )
+ model_name = probe_model_id(base_url) or expected_model_name
+ if model_name != expected_model_name:
+ raise SpawnError(
+ f"Model mismatch at {base_url}: expected {expected_model_name!r}, got {model_name!r}. "
+ "Stop the running server or unset SURYA_INFERENCE_URL."
+ )
+ return SpawnedServer(
+ base_url=base_url,
+ health_url=health_url,
+ model_name=model_name,
+ pid=None,
+ backend=backend,
+ spawned_by_us=False,
+ )
+
+ # 1. Probe sentinel without lock
+ existing = _read_sentinel(backend)
+ if existing:
+ port = existing.get("port")
+ pid = existing.get("pid")
+ if port and probe_health(health_url_for(port)):
+ running_model = probe_model_id(openai_url_for(port)) or expected_model_name
+ if running_model != expected_model_name:
+ raise SpawnError(
+ f"Existing {backend} server on port {port} serves {running_model!r}, "
+ f"expected {expected_model_name!r}. Stop it before continuing."
+ )
+ logger.info(f"Attaching to existing {backend} server on port {port}")
+ return SpawnedServer(
+ base_url=openai_url_for(port),
+ health_url=health_url_for(port),
+ model_name=running_model,
+ pid=pid,
+ backend=backend,
+ spawned_by_us=False,
+ )
+ else:
+ _delete_sentinel(backend)
+
+ if not settings.SURYA_INFERENCE_AUTOSTART:
+ raise SpawnError(
+ f"No running {backend} server and SURYA_INFERENCE_AUTOSTART is False. "
+ "Set the variable to True or start the server manually."
+ )
+
+ # 2. Acquire filelock to prevent races
+ try:
+ from filelock import FileLock
+ except ImportError as e:
+ raise SpawnError(
+ "filelock is required for server spawn. pip install filelock"
+ ) from e
+
+ lock = FileLock(str(_lock_path(backend)), timeout=120)
+ with lock:
+ # Re-check sentinel inside the lock
+ existing = _read_sentinel(backend)
+ if existing:
+ port = existing.get("port")
+ if port and probe_health(health_url_for(port)):
+ running_model = (
+ probe_model_id(openai_url_for(port)) or expected_model_name
+ )
+ if running_model != expected_model_name:
+ raise SpawnError(
+ f"Existing {backend} server on port {port} serves {running_model!r}, "
+ f"expected {expected_model_name!r}."
+ )
+ return SpawnedServer(
+ base_url=openai_url_for(port),
+ health_url=health_url_for(port),
+ model_name=running_model,
+ pid=existing.get("pid"),
+ backend=backend,
+ spawned_by_us=False,
+ )
+
+ # 3. Spawn fresh
+ port = settings.SURYA_INFERENCE_PORT or find_free_port()
+ logger.info(f"Spawning {backend} server on port {port}")
+ spawn_handle = spawn_fn(port)
+
+ # 4. Write sentinel
+ _write_sentinel(
+ backend,
+ {
+ "port": port,
+ "pid": spawn_handle.pid,
+ "model": expected_model_name,
+ "backend": backend,
+ "cleanup_id": spawn_handle.cleanup_id,
+ "cleanup_kind": spawn_handle.cleanup_kind,
+ },
+ )
+
+ # 5. Register atexit cleanup (only spawner). Skipped when keep-alive is
+ # set so the server outlives this process and later commands attach to
+ # it via the sentinel. (_cleanup is still callable below on startup
+ # failure, where we always tear a half-started server down.)
+ def _cleanup():
+ try:
+ if spawn_handle.cleanup_kind == "docker":
+ _stop_docker_container(spawn_handle.cleanup_id)
+ elif spawn_handle.cleanup_kind == "process":
+ if spawn_handle.pid:
+ _stop_process(spawn_handle.pid, backend)
+ finally:
+ _delete_sentinel(backend)
+
+ if settings.SURYA_INFERENCE_KEEP_ALIVE:
+ logger.info(
+ f"keep-alive: {backend} server on port {port} will stay up "
+ f"after exit (cleanup_id={spawn_handle.cleanup_id!r})"
+ )
+ else:
+ atexit.register(_cleanup)
+
+ # 6. Wait for health
+ health_url = health_url_for(port)
+ if not wait_for_health(health_url, total_timeout=startup_timeout):
+ # Grab the server's own logs *before* cleanup tears the (--rm)
+ # container down, otherwise the actual failure reason is lost and
+ # all the caller sees is this timeout.
+ logs = _capture_server_logs(spawn_handle)
+ _cleanup()
+ raise SpawnError(
+ f"{backend} server failed to become healthy at {health_url} "
+ f"within {startup_timeout}s.\n"
+ f"--- last {backend} server logs ---\n{logs}"
+ )
+
+ # 7. Verify model name
+ running_model = probe_model_id(openai_url_for(port))
+ if running_model and running_model != expected_model_name:
+ logger.warning(
+ f"{backend} server reports model={running_model!r} "
+ f"but expected {expected_model_name!r}; using reported name."
+ )
+ expected_model_name = running_model
+
+ logger.info(
+ f"{backend} server ready on port {port} (model={expected_model_name})"
+ )
+ return SpawnedServer(
+ base_url=openai_url_for(port),
+ health_url=health_url,
+ model_name=expected_model_name,
+ pid=spawn_handle.pid,
+ backend=backend,
+ spawned_by_us=True,
+ )
+
+
+@dataclass
+class SpawnHandle:
+ pid: Optional[int]
+ cleanup_id: str # container name for docker, str(pid) for process
+ cleanup_kind: str # "docker" | "process"
diff --git a/surya/inference/backends/vllm.py b/surya/inference/backends/vllm.py
new file mode 100644
index 00000000..ee9e2512
--- /dev/null
+++ b/surya/inference/backends/vllm.py
@@ -0,0 +1,207 @@
+"""vllm backend: spawns the vllm/vllm-openai docker image with MTP=2."""
+
+from __future__ import annotations
+
+import json
+import math
+import os
+import shutil
+import subprocess
+from typing import List, Optional
+
+from openai import OpenAI
+
+from surya.inference.backends.base import Backend, ServerHandle
+from surya.inference.backends.openai_client import chat_completions_batch
+from surya.inference.backends.spawn import (
+ SpawnHandle,
+ SpawnError,
+ attach_or_spawn,
+)
+from surya.inference.schema import BatchInputItem, BatchOutputItem
+from surya.logging import get_logger
+from surya.settings import settings
+
+logger = get_logger()
+
+
+# 24GB baseline (re-tune for surya-2 once benchmarks land)
+BASELINE_VRAM_GB = 24
+BASELINE_MAX_BATCHED_TOKENS = 8192
+BASELINE_MAX_NUM_SEQS = 32
+
+GPU_VRAM_GB = {
+ "b300": 270,
+ "b200": 180,
+ "h200": 141,
+ "h100": 80,
+ "a100-80": 80,
+ "a100": 40,
+ "a100-40": 40,
+ "l40s": 48,
+ "a10": 24,
+ "l4": 24,
+ "5090": 32,
+ "4090": 24,
+ "3090": 24,
+ "t4": 16,
+}
+
+
+def _gpu_settings(gpu: str) -> tuple[int, int]:
+ vram = GPU_VRAM_GB.get(gpu)
+ if vram is None:
+ available = ", ".join(sorted(GPU_VRAM_GB.keys()))
+ raise SpawnError(f"Unknown VLLM_GPU_TYPE {gpu!r}. Available: {available}")
+ ratio = vram / BASELINE_VRAM_GB
+ raw_tokens = BASELINE_MAX_BATCHED_TOKENS * ratio
+ max_batched_tokens = max(1024, 2 ** math.floor(math.log2(raw_tokens)))
+ max_num_seqs = max(8, (int(BASELINE_MAX_NUM_SEQS * ratio) // 8) * 8)
+ return max_batched_tokens, max_num_seqs
+
+
+def _resolve_docker_binary() -> str:
+ found = shutil.which("docker")
+ if found:
+ return found
+ raise SpawnError(
+ "docker binary not found. Install Docker (https://docs.docker.com/get-docker/) "
+ "and ensure the daemon is running."
+ )
+
+
+def _health_url(port: int) -> str:
+ return f"http://{settings.SURYA_INFERENCE_HOST}:{port}"
+
+
+def _openai_url(port: int) -> str:
+ return f"http://{settings.SURYA_INFERENCE_HOST}:{port}/v1"
+
+
+class VllmBackend(Backend):
+ name = "vllm"
+
+ def __init__(self):
+ self.handle: Optional[ServerHandle] = None
+ self._client: Optional[OpenAI] = None
+
+ def start(self) -> ServerHandle:
+ if self.handle is not None:
+ return self.handle
+
+ # If user pinned an external server, attach without spawning docker.
+ if settings.SURYA_INFERENCE_URL:
+ spawned = attach_or_spawn(
+ backend=self.name,
+ expected_model_name=settings.SURYA_MODEL_CHECKPOINT,
+ spawn_fn=lambda port: SpawnHandle(
+ pid=None, cleanup_id="", cleanup_kind="docker"
+ ),
+ health_url_for=_health_url,
+ openai_url_for=_openai_url,
+ startup_timeout=settings.SURYA_INFERENCE_STARTUP_TIMEOUT,
+ )
+ self.handle = ServerHandle(
+ base_url=spawned.base_url,
+ model_name=spawned.model_name,
+ spawned_by_us=spawned.spawned_by_us,
+ )
+ self._client = OpenAI(
+ api_key=settings.VLLM_API_KEY, base_url=self.handle.base_url
+ )
+ return self.handle
+
+ docker = _resolve_docker_binary()
+ max_batched_tokens, max_num_seqs = _gpu_settings(settings.VLLM_GPU_TYPE)
+
+ def spawn_fn(port: int) -> SpawnHandle:
+ container_name = f"surya-vllm-{port}"
+ hf_cache = os.path.expanduser(settings.DOCKER_HF_CACHE_PATH)
+ cmd = [
+ docker,
+ "run",
+ "--rm",
+ "-d",
+ "--name",
+ container_name,
+ "--runtime",
+ "nvidia",
+ "--gpus",
+ f"device={settings.VLLM_GPUS}",
+ "-v",
+ f"{hf_cache}:/root/.cache/huggingface",
+ "-p",
+ f"{port}:8000",
+ "--ipc=host",
+ settings.VLLM_DOCKER_IMAGE,
+ "--model",
+ settings.SURYA_MODEL_CHECKPOINT,
+ "--no-enforce-eager",
+ "--max-num-seqs",
+ str(max_num_seqs),
+ "--dtype",
+ settings.VLLM_DTYPE,
+ "--max-model-len",
+ str(settings.VLLM_MAX_MODEL_LEN),
+ "--max-num-batched-tokens",
+ str(max_batched_tokens),
+ "--gpu-memory-utilization",
+ str(settings.VLLM_GPU_MEMORY_UTILIZATION),
+ "--enable-prefix-caching",
+ "--mm-processor-kwargs",
+ json.dumps({"min_pixels": 3136, "max_pixels": 6291456}),
+ "--served-model-name",
+ settings.SURYA_MODEL_CHECKPOINT,
+ ]
+ if settings.VLLM_ENABLE_MTP:
+ spec_config = json.dumps(
+ {
+ "method": "mtp",
+ "num_speculative_tokens": settings.VLLM_MTP_TOKENS,
+ }
+ )
+ cmd.extend(["--speculative-config", spec_config])
+ for extra in (settings.VLLM_EXTRA_ARGS or "").split():
+ cmd.append(extra)
+ logger.info(f"Spawning: {' '.join(cmd)}")
+ result = subprocess.run(cmd, capture_output=True, text=True, check=False)
+ if result.returncode != 0:
+ raise SpawnError(f"docker run failed: {result.stderr or result.stdout}")
+ return SpawnHandle(
+ pid=None, cleanup_id=container_name, cleanup_kind="docker"
+ )
+
+ spawned = attach_or_spawn(
+ backend=self.name,
+ expected_model_name=settings.SURYA_MODEL_CHECKPOINT,
+ spawn_fn=spawn_fn,
+ health_url_for=_health_url,
+ openai_url_for=_openai_url,
+ startup_timeout=settings.SURYA_INFERENCE_STARTUP_TIMEOUT,
+ )
+ self.handle = ServerHandle(
+ base_url=spawned.base_url,
+ model_name=spawned.model_name,
+ spawned_by_us=spawned.spawned_by_us,
+ )
+ self._client = OpenAI(
+ api_key=settings.VLLM_API_KEY,
+ base_url=self.handle.base_url,
+ )
+ return self.handle
+
+ def stop(self) -> None:
+ self.handle = None
+ self._client = None
+
+ def generate(self, batch: List[BatchInputItem]) -> List[BatchOutputItem]:
+ if self.handle is None or self._client is None:
+ self.start()
+ return chat_completions_batch(
+ batch,
+ client=self._client,
+ model_name=self.handle.model_name,
+ timeout=settings.SURYA_INFERENCE_TIMEOUT_SECONDS,
+ max_workers=settings.SURYA_INFERENCE_PARALLEL,
+ request_logprobs_default=settings.SURYA_INFERENCE_LOGPROBS,
+ )
diff --git a/surya/inference/parsers.py b/surya/inference/parsers.py
new file mode 100644
index 00000000..728d3a62
--- /dev/null
+++ b/surya/inference/parsers.py
@@ -0,0 +1,182 @@
+"""Parsers for the three task outputs."""
+
+from __future__ import annotations
+
+import json
+import re
+from dataclasses import dataclass
+from typing import List, Tuple
+
+
+from surya.logging import get_logger
+
+logger = get_logger()
+
+
+# ---- Layout (LAYOUT_PROMPT) -------------------------------------------------
+
+
+@dataclass
+class ParsedLayoutBlock:
+ label: str
+ bbox: Tuple[float, float, float, float] # 0-1000 normalized
+ count: int # multiple of 50, model's token estimate
+
+
+_JSON_ARRAY_RE = re.compile(r"\[.*\]", re.DOTALL)
+
+
+def _strip_fences(text: str) -> str:
+ cleaned = text.strip()
+ if cleaned.startswith("```"):
+ cleaned = re.sub(r"^```[a-zA-Z]*\n", "", cleaned)
+ cleaned = re.sub(r"\n```\s*$", "", cleaned)
+ return cleaned
+
+
+def _coerce_bbox(bbox) -> Tuple[float, float, float, float]:
+ if isinstance(bbox, str):
+ parts = [float(x) for x in bbox.replace(",", " ").split()]
+ else:
+ parts = [float(x) for x in bbox]
+ if len(parts) != 4:
+ raise ValueError(f"Bad bbox: {bbox!r}")
+ return (parts[0], parts[1], parts[2], parts[3])
+
+
+def _coerce_count(value) -> int:
+ if value is None:
+ return 0
+ try:
+ return max(0, int(value))
+ except (TypeError, ValueError):
+ return 0
+
+
+def parse_layout(text: str) -> List[ParsedLayoutBlock]:
+ """Pull the JSON array out of LAYOUT_PROMPT output and convert to typed blocks.
+
+ Tolerates code fences, missing fields, and stringified bboxes.
+ """
+ cleaned = _strip_fences(text)
+ m = _JSON_ARRAY_RE.search(cleaned)
+ if not m:
+ raise ValueError(f"No JSON array found in layout output: {text[:500]!r}")
+ raw = json.loads(m.group(0))
+ out: List[ParsedLayoutBlock] = []
+ for item in raw:
+ try:
+ bbox = _coerce_bbox(item["bbox"])
+ except (KeyError, ValueError) as e:
+ logger.warning(f"Skipping layout block with bad bbox: {e}")
+ continue
+ label = str(item.get("label", "block"))
+ count = _coerce_count(item.get("count"))
+ out.append(ParsedLayoutBlock(label=label, bbox=bbox, count=count))
+ return out
+
+
+# ---- Table rec (TABLE_REC_PROMPT) ------------------------------------------
+
+
+@dataclass
+class ParsedTableElement:
+ label: str # "Row" or "Col"
+ bbox: Tuple[float, float, float, float]
+
+
+def parse_table_rec(text: str) -> List[ParsedTableElement]:
+ """Parse JSON array of {label: "Row"|"Col", bbox: "x0 y0 x1 y1"} from
+ TABLE_REC_PROMPT output. Returns a flat list of Row + Col elements;
+ cell derivation is the caller's job."""
+ cleaned = _strip_fences(text)
+ m = _JSON_ARRAY_RE.search(cleaned)
+ if not m:
+ raise ValueError(f"No JSON array found in table_rec output: {text[:500]!r}")
+ raw = json.loads(m.group(0))
+ out: List[ParsedTableElement] = []
+ for item in raw:
+ label = str(item.get("label", "")).strip()
+ if label not in ("Row", "Col"):
+ continue
+ try:
+ bbox = _coerce_bbox(item["bbox"])
+ except (KeyError, ValueError):
+ continue
+ out.append(ParsedTableElement(label=label, bbox=bbox))
+ return out
+
+
+# ---- Block HTML (BLOCK_PROMPT for full table path / general block path) ---
+
+
+def clean_block_html(html: str) -> str:
+ """Light cleanup of model-emitted HTML for a single block.
+
+ Strips code fences, leading/trailing whitespace. Does NOT validate against
+ ALLOWED_TAGS — the model is expected to comply, and downstream consumers
+ can sanitize further if needed.
+ """
+ cleaned = _strip_fences(html).strip()
+ return cleaned
+
+
+# ---- Full-page fallback (HIGH_ACCURACY_BBOX_PROMPT) -----------------------
+
+
+@dataclass
+class ParsedFullPageBlock:
+ label: str
+ bbox: Tuple[float, float, float, float] # 0-1000 normalized
+ html: str # inner HTML of the wrapping div
+
+
+def parse_full_page_html(text: str) -> List[ParsedFullPageBlock]:
+ """Parse output of HIGH_ACCURACY_BBOX_PROMPT — top-level inner HTML
blocks. Returns one entry per top-level div."""
+ from bs4 import BeautifulSoup
+
+ cleaned = _strip_fences(text).strip()
+ if not cleaned:
+ return []
+ # The model outputs a sequence of top-level divs (no surrounding root).
+ # BeautifulSoup parses fine without one.
+ soup = BeautifulSoup(cleaned, "html.parser")
+ divs = soup.find_all("div", recursive=False)
+ out: List[ParsedFullPageBlock] = []
+ for div in divs:
+ label = div.get("data-label")
+ bbox_str = div.get("data-bbox")
+ if not label or not bbox_str:
+ continue
+ try:
+ parts = [float(x) for x in bbox_str.split()]
+ except ValueError:
+ continue
+ if len(parts) != 4:
+ continue
+ # Strip nested data-bbox attrs from the inner HTML so downstream
+ # consumers don't see model debug info on every child element.
+ for tag in div.find_all(attrs={"data-bbox": True}):
+ del tag["data-bbox"]
+ for tag in div.find_all(attrs={"data-label": True}):
+ del tag["data-label"]
+ inner = "".join(str(c) for c in div.contents).strip()
+ out.append(
+ ParsedFullPageBlock(
+ label=str(label),
+ bbox=(parts[0], parts[1], parts[2], parts[3]),
+ html=inner,
+ )
+ )
+ return out
+
+
+def denorm_bbox(bbox, img_w: int, img_h: int, scale: int = 1000):
+ x0, y0, x1, y1 = bbox
+ return (
+ x0 / scale * img_w,
+ y0 / scale * img_h,
+ x1 / scale * img_w,
+ y1 / scale * img_h,
+ )
diff --git a/surya/inference/prompts.py b/surya/inference/prompts.py
new file mode 100644
index 00000000..c9a14253
--- /dev/null
+++ b/surya/inference/prompts.py
@@ -0,0 +1,158 @@
+"""Prompt strings for surya2. The exact wording is the model's training-time
+contract — do not paraphrase without retraining."""
+
+from surya.inference.schema import PROMPT_TYPE_BLOCK as PROMPT_TYPE_BLOCK
+from surya.inference.schema import (
+ PROMPT_TYPE_HIGH_ACCURACY_BBOX as PROMPT_TYPE_HIGH_ACCURACY_BBOX,
+)
+from surya.inference.schema import PROMPT_TYPE_LAYOUT as PROMPT_TYPE_LAYOUT
+from surya.inference.schema import PROMPT_TYPE_TABLE_REC as PROMPT_TYPE_TABLE_REC
+
+ALLOWED_TAGS = [
+ "math",
+ "br",
+ "i",
+ "b",
+ "u",
+ "del",
+ "sup",
+ "sub",
+ "table",
+ "tr",
+ "td",
+ "p",
+ "th",
+ "div",
+ "pre",
+ "h1",
+ "h2",
+ "h3",
+ "h4",
+ "h5",
+ "ul",
+ "ol",
+ "li",
+ "input",
+ "a",
+ "span",
+ "img",
+ "hr",
+ "tbody",
+ "small",
+ "caption",
+ "strong",
+ "thead",
+ "big",
+ "code",
+ "chem",
+]
+
+ALLOWED_ATTRIBUTES = [
+ "class",
+ "colspan",
+ "rowspan",
+ "display",
+ "checked",
+ "type",
+ "border",
+ "value",
+ "style",
+ "href",
+ "alt",
+ "align",
+ "data-bbox",
+ "data-label",
+]
+
+# Block labels we don't run OCR on.
+SKIP_OCR_LABELS = {"Figure", "Image", "Diagram", "Blank-Page"}
+
+LAYOUT_PROMPT = (
+ "Output the layout of this image as JSON. Each entry is a dict with "
+ '"label", "bbox", and "count" fields. Bbox is x0 y0 x1 y1, normalized 0-1000.'
+)
+
+BLOCK_PROMPT = "OCR this block image to HTML."
+
+TABLE_REC_PROMPT = (
+ "Output the table rows then columns as JSON. Each entry is a dict with "
+ '"label" ("Row" or "Col") and "bbox" (x0 y0 x1 y1, normalized 0-1000).'
+)
+
+HIGH_ACCURACY_BBOX_PROMPT = (
+ "OCR this image to HTML. Each block is a div with data-label and data-bbox "
+ "(x0 y0 x1 y1, normalized 0-1000)."
+)
+
+
+PROMPT_MAPPING = {
+ "layout": LAYOUT_PROMPT,
+ "block": BLOCK_PROMPT,
+ "table_rec": TABLE_REC_PROMPT,
+ "high_accuracy_bbox": HIGH_ACCURACY_BBOX_PROMPT,
+}
+
+
+# JSON schema for LAYOUT_PROMPT — enforced via vllm guided decoding so the
+# model can't emit malformed JSON. bbox is a "x0 y0 x1 y1" string (model's
+# training-time format); count is a non-negative integer.
+LAYOUT_LABEL_SET = [
+ "Caption",
+ "Footnote",
+ "Equation-Block",
+ "List-Group",
+ "Page-Header",
+ "Page-Footer",
+ "Image",
+ "Section-Header",
+ "Table",
+ "Text",
+ "Complex-Block",
+ "Code-Block",
+ "Form",
+ "Table-Of-Contents",
+ "Figure",
+ "Chemical-Block",
+ "Diagram",
+ "Bibliography",
+ "Blank-Page",
+]
+
+LAYOUT_JSON_SCHEMA = {
+ "type": "array",
+ "maxItems": 200,
+ "items": {
+ "type": "object",
+ "properties": {
+ "label": {"type": "string", "enum": LAYOUT_LABEL_SET},
+ "bbox": {
+ "type": "string",
+ "pattern": r"^\d{1,4} \d{1,4} \d{1,4} \d{1,4}$",
+ },
+ "count": {"type": "integer", "minimum": 0, "maximum": 10000},
+ },
+ "required": ["label", "bbox", "count"],
+ "additionalProperties": False,
+ },
+}
+
+
+# JSON schema for TABLE_REC_PROMPT — array of {label: Row|Col, bbox: "x0 y0 x1 y1"}.
+TABLE_REC_LABEL_SET = ["Row", "Col"]
+
+TABLE_REC_JSON_SCHEMA = {
+ "type": "array",
+ "maxItems": 200,
+ "items": {
+ "type": "object",
+ "properties": {
+ "label": {"type": "string", "enum": TABLE_REC_LABEL_SET},
+ "bbox": {
+ "type": "string",
+ "pattern": r"^\d{1,4} \d{1,4} \d{1,4} \d{1,4}$",
+ },
+ },
+ "required": ["label", "bbox"],
+ "additionalProperties": False,
+ },
+}
diff --git a/surya/inference/schema.py b/surya/inference/schema.py
new file mode 100644
index 00000000..f171da5a
--- /dev/null
+++ b/surya/inference/schema.py
@@ -0,0 +1,45 @@
+from dataclasses import dataclass, field
+from typing import Any, List, Optional
+
+from PIL import Image
+
+
+PROMPT_TYPE_LAYOUT = "layout"
+PROMPT_TYPE_BLOCK = "block"
+PROMPT_TYPE_TABLE_REC = "table_rec"
+PROMPT_TYPE_HIGH_ACCURACY_BBOX = "high_accuracy_bbox"
+
+
+@dataclass
+class BatchInputItem:
+ image: Image.Image
+ prompt_type: str
+ prompt: Optional[str] = None # If set, overrides the default prompt for prompt_type
+ max_tokens: Optional[int] = None
+ request_logprobs: bool = False
+ # vllm-native guided decoding — JSON schema, regex, or grammar string.
+ # When set, the server constrains the decode tokens to match the schema.
+ guided_json: Optional[dict] = None
+ guided_regex: Optional[str] = None
+ metadata: dict = field(default_factory=dict) # Free-form, passes through to output
+
+
+@dataclass
+class GenerationResult:
+ raw: str
+ token_count: int
+ error: bool = False
+ # Mean of exp(logprob) across response tokens, if logprobs requested
+ mean_token_prob: Optional[float] = None
+ # Per-token logprobs (raw OpenAI-style content list), if requested - phase 2 use
+ logprobs: Optional[List[Any]] = None
+
+
+@dataclass
+class BatchOutputItem:
+ raw: str
+ token_count: int
+ error: bool
+ mean_token_prob: Optional[float] = None
+ logprobs: Optional[List[Any]] = None
+ metadata: dict = field(default_factory=dict)
diff --git a/surya/inference/util.py b/surya/inference/util.py
new file mode 100644
index 00000000..16911704
--- /dev/null
+++ b/surya/inference/util.py
@@ -0,0 +1,96 @@
+from typing import Tuple
+
+from PIL import Image
+
+
+def scale_to_fit(
+ img: Image.Image,
+ max_size: Tuple[int, int] = (3072, 2048),
+ min_size: Tuple[int, int] = (1792, 28),
+ grid_size: int = 28,
+) -> Image.Image:
+ resample_method = Image.Resampling.LANCZOS
+
+ width, height = img.size
+
+ if width <= 0 or height <= 0:
+ return img
+
+ original_ar = width / height
+ current_pixels = width * height
+ max_pixels = max_size[0] * max_size[1]
+ min_pixels = min_size[0] * min_size[1]
+
+ scale = 1.0
+ if current_pixels > max_pixels:
+ scale = (max_pixels / current_pixels) ** 0.5
+ elif current_pixels < min_pixels:
+ scale = (min_pixels / current_pixels) ** 0.5
+
+ w_blocks = max(1, round((width * scale) / grid_size))
+ h_blocks = max(1, round((height * scale) / grid_size))
+
+ while (w_blocks * h_blocks * grid_size * grid_size) > max_pixels:
+ if w_blocks == 1 and h_blocks == 1:
+ break
+
+ if w_blocks == 1:
+ h_blocks -= 1
+ continue
+ if h_blocks == 1:
+ w_blocks -= 1
+ continue
+
+ ar_w_loss = abs(((w_blocks - 1) / h_blocks) - original_ar)
+ ar_h_loss = abs((w_blocks / (h_blocks - 1)) - original_ar)
+
+ if ar_w_loss < ar_h_loss:
+ w_blocks -= 1
+ else:
+ h_blocks -= 1
+
+ new_width = w_blocks * grid_size
+ new_height = h_blocks * grid_size
+
+ if (new_width, new_height) == (width, height):
+ return img
+
+ return img.resize((new_width, new_height), resample=resample_method)
+
+
+def detect_repeat_token(
+ predicted_tokens: str,
+ base_max_repeats: int = 4,
+ window_size: int = 500,
+ cut_from_end: int = 0,
+ scaling_factor: float = 3.0,
+) -> bool:
+ if cut_from_end > 0:
+ predicted_tokens = predicted_tokens[:-cut_from_end]
+
+ for seq_len in range(1, window_size // 2 + 1):
+ candidate_seq = predicted_tokens[-seq_len:]
+
+ max_repeats = int(base_max_repeats * (1 + scaling_factor / seq_len))
+
+ repeat_count = 0
+ pos = len(predicted_tokens) - seq_len
+ if pos < 0:
+ continue
+
+ while pos >= 0:
+ if predicted_tokens[pos : pos + seq_len] == candidate_seq:
+ repeat_count += 1
+ pos -= seq_len
+ else:
+ break
+
+ if repeat_count > max_repeats:
+ return True
+
+ return False
+
+
+def image_token_budget(block_count: int, ceiling: int = 4096, floor: int = 64) -> int:
+ """Per-block max_tokens: count + 100, clamped to [floor, ceiling]."""
+ return min(max(block_count + 100, floor), ceiling)
diff --git a/surya/input/load.py b/surya/input/load.py
index 052e7049..4b5fd27b 100644
--- a/surya/input/load.py
+++ b/surya/input/load.py
@@ -7,7 +7,6 @@
import os
import filetype
from PIL import Image
-import json
logger = get_logger()
@@ -76,9 +75,3 @@ def load_from_folder(
logger.warning(f"Could not load image {path}")
continue
return images, names
-
-
-def load_lang_file(lang_path, names):
- with open(lang_path, "r") as f:
- lang_dict = json.load(f)
- return [lang_dict[name].copy() for name in names]
diff --git a/surya/input/processing.py b/surya/input/processing.py
index ca1179d2..03a6003d 100644
--- a/surya/input/processing.py
+++ b/surya/input/processing.py
@@ -1,24 +1,9 @@
from typing import List
-import cv2
-import numpy as np
import pypdfium2
-from PIL import Image
-from surya.logging import get_logger
from surya.settings import settings
-logger = get_logger()
-
-
-def convert_if_not_rgb(images: List[Image.Image]) -> List[Image.Image]:
- new_images = []
- for image in images:
- if image.mode != "RGB":
- image = image.convert("RGB")
- new_images.append(image)
- return new_images
-
def open_pdf(pdf_filepath):
return pypdfium2.PdfDocument(pdf_filepath)
@@ -30,72 +15,3 @@ def get_page_images(doc, indices: List, dpi=settings.IMAGE_DPI):
]
images = [image.convert("RGB") for image in images]
return images
-
-
-def slice_bboxes_from_image(image: np.ndarray, bboxes):
- lines = []
- for bbox in bboxes:
- bbox = np.array(bbox, dtype=np.int32)
- bbox = np.clip(bbox, 0, None) # Ensure no negative indices
- # Ensure bbox is within the image bounds
- if bbox[3] <= bbox[1]:
- bbox[3] = bbox[1] + 1
-
- if bbox[2] <= bbox[0]:
- bbox[2] = bbox[0] + 1
-
- bbox[2] = min(bbox[2], image.shape[1])
- bbox[3] = min(bbox[3], image.shape[0])
-
- line = image[bbox[1] : bbox[3], bbox[0] : bbox[2]].copy()
- if line.size == 0:
- logger.warning(f"Warning: found an empty line with bbox {bbox}")
- lines.append(line)
- return lines
-
-
-def slice_polys_from_image(image: np.ndarray, polys):
- lines = []
- for idx, poly in enumerate(polys):
- lines.append(slice_and_pad_poly(image, poly))
- return lines
-
-
-def slice_and_pad_poly(image_array: np.array, coordinates):
- # Draw polygon onto mask
- coordinates = [(corner[0], corner[1]) for corner in coordinates]
- bbox = [
- min([x[0] for x in coordinates]),
- min([x[1] for x in coordinates]),
- max([x[0] for x in coordinates]),
- max([x[1] for x in coordinates]),
- ]
-
- # We mask out anything not in the polygon
- cropped_polygon = image_array[bbox[1] : bbox[3], bbox[0] : bbox[2]].copy()
- height, width = cropped_polygon.shape[:2]
-
- coordinates = [(x - bbox[0], y - bbox[1]) for x, y in coordinates]
-
- # Validate the cropped area
- if any(
- [
- bbox[3] <= bbox[1] or bbox[2] <= bbox[0],
- len(coordinates) < 3,
- height == 0,
- width == 0,
- ]
- ):
- return cropped_polygon
-
- # Pad the area outside the polygon with the pad value
- try:
- mask = np.zeros(cropped_polygon.shape[:2], dtype=np.uint8)
- cv2.fillPoly(mask, [np.int32(coordinates)], 1)
- mask = np.stack([mask] * 3, axis=-1)
-
- cropped_polygon[mask == 0] = settings.RECOGNITION_PAD_VALUE
- except cv2.error as e:
- logger.warning(f"Warning: issue while processing polygon: {e}")
-
- return cropped_polygon
diff --git a/surya/layout/__init__.py b/surya/layout/__init__.py
index 6e8b98ae..d9decb78 100644
--- a/surya/layout/__init__.py
+++ b/surya/layout/__init__.py
@@ -1,111 +1,137 @@
-from typing import List
+from __future__ import annotations
+
+from typing import List, Optional
from PIL import Image
-from surya.common.predictor import BasePredictor
+from surya.common.blank import is_blank_region
+from surya.inference import SuryaInferenceManager, get_default_manager
+from surya.inference.parsers import denorm_bbox, parse_layout
+from surya.inference.prompts import LAYOUT_JSON_SCHEMA, PROMPT_TYPE_LAYOUT
+from surya.inference.schema import BatchInputItem
+from surya.layout.label import LAYOUT_PRED_RELABEL, TEXT_LABELS
from surya.layout.schema import LayoutBox, LayoutResult
+from surya.logging import get_logger
from surya.settings import settings
-from surya.foundation import FoundationPredictor, TaskNames
-from surya.foundation.util import prediction_to_polygon_batch
-from surya.input.processing import convert_if_not_rgb
-from surya.layout.label import LAYOUT_PRED_RELABEL
-from surya.common.util import clean_boxes
-
-
-class LayoutPredictor(BasePredictor):
- batch_size = settings.LAYOUT_BATCH_SIZE
- default_batch_sizes = {"cpu": 4, "mps": 4, "cuda": 32, "xla": 16}
-
- # Override base init - Do not load model
- def __init__(self, foundation_predictor: FoundationPredictor):
- self.foundation_predictor = foundation_predictor
- self.processor = self.foundation_predictor.processor
- self.bbox_size = self.foundation_predictor.model.config.bbox_size
- self.tasks = self.foundation_predictor.tasks
-
- # Special handling for disable tqdm to pass into foundation predictor
- # Make sure they are kept in sync
+
+logger = get_logger()
+
+
+class LayoutPredictor:
+ """Run LAYOUT_PROMPT on full pages, parse JSON, return LayoutResult per image."""
+
+ def __init__(self, manager: Optional[SuryaInferenceManager] = None):
+ self.manager = manager # If None, get_default_manager() is used at call time
+ self._disable_tqdm = settings.DISABLE_TQDM
+
@property
def disable_tqdm(self) -> bool:
- return super().disable_tqdm
+ return self._disable_tqdm
@disable_tqdm.setter
def disable_tqdm(self, value: bool) -> None:
self._disable_tqdm = bool(value)
- self.foundation_predictor.disable_tqdm = bool(value)
+
+ def to(self, *args, **kwargs):
+ # Manager-backed; .to() is a no-op for compatibility with BasePredictor callers.
+ return
def __call__(
- self, images: List[Image.Image], batch_size: int | None = None, top_k: int = 5
+ self,
+ images: List[Image.Image],
+ target_image_sizes: Optional[List[tuple]] = None,
+ max_tokens: Optional[int] = None,
) -> List[LayoutResult]:
- assert all([isinstance(image, Image.Image) for image in images])
- if batch_size is None:
- batch_size = self.get_batch_size()
+ """Run layout on a batch of images.
- if len(images) == 0:
+ target_image_sizes: optional list of (width, height) tuples — if
+ provided, bboxes are denormalized to these sizes instead of each
+ input image's size. Useful when layout runs on a low-DPI render but
+ you want bboxes in the OCR image's coordinate space.
+ """
+ if not images:
return []
+ manager = self.manager or get_default_manager()
- images = convert_if_not_rgb(images)
- images = [self.processor.image_processor(image) for image in images]
-
- predicted_tokens, batch_bboxes, scores, topk_scores = (
- self.foundation_predictor.prediction_loop(
- images=images,
- input_texts=["" for _ in range(len(images))],
- task_names=[TaskNames.layout for _ in range(len(images))],
- batch_size=batch_size,
- max_lookahead_tokens=0, # Do not do MTP for layout
- top_k=5,
- max_sliding_window=576,
- max_tokens=500,
- tqdm_desc="Recognizing Layout"
+ max_tokens = max_tokens or settings.SURYA_MAX_TOKENS_LAYOUT
+ guided = LAYOUT_JSON_SCHEMA if settings.SURYA_GUIDED_LAYOUT else None
+ batch = [
+ BatchInputItem(
+ image=img,
+ prompt_type=PROMPT_TYPE_LAYOUT,
+ max_tokens=max_tokens,
+ guided_json=guided,
)
- )
-
- image_sizes = [img.shape for img in images]
- predicted_polygons = prediction_to_polygon_batch(
- batch_bboxes, image_sizes, self.bbox_size, self.bbox_size // 2
- )
- layout_results = []
- for image, image_tokens, image_polygons, image_scores, image_topk_scores in zip(
- images, predicted_tokens, predicted_polygons, scores, topk_scores
- ):
- layout_boxes = []
- for z, (tok, poly, score, tok_topk) in enumerate(
- zip(image_tokens, image_polygons, image_scores, image_topk_scores)
- ):
- if tok == self.processor.eos_token_id:
- break
-
- predicted_label = self.processor.decode([tok], "layout")
- label = LAYOUT_PRED_RELABEL.get(predicted_label)
- if not label:
- # Layout can sometimes return unknown labels from other objectives
- continue
-
- top_k_dict = {}
- for k, v in tok_topk.items():
- topk_label = self.processor.decode([k], "layout")
- if topk_label in LAYOUT_PRED_RELABEL:
- topk_label = LAYOUT_PRED_RELABEL[topk_label]
- if not topk_label.strip():
- continue
- top_k_dict.update({topk_label: v})
- layout_boxes.append(
+ for img in images
+ ]
+ outputs = manager.generate(batch)
+
+ if target_image_sizes is not None and len(target_image_sizes) != len(images):
+ raise ValueError("target_image_sizes must match images length")
+
+ results: List[LayoutResult] = []
+ for idx, (img, out) in enumerate(zip(images, outputs)):
+ if target_image_sizes is not None:
+ w, h = target_image_sizes[idx]
+ else:
+ w, h = img.size
+ page_bbox = [0, 0, float(w), float(h)]
+ if out.error or not out.raw:
+ results.append(
+ LayoutResult(
+ bboxes=[], image_bbox=page_bbox, raw=out.raw, error=True
+ )
+ )
+ continue
+ try:
+ parsed = parse_layout(out.raw)
+ except Exception as e:
+ logger.warning(f"Layout parse failed: {e}; raw[:300]={out.raw[:300]!r}")
+ results.append(
+ LayoutResult(
+ bboxes=[], image_bbox=page_bbox, raw=out.raw, error=True
+ )
+ )
+ continue
+
+ confidence = out.mean_token_prob if out.mean_token_prob is not None else 1.0
+ img_w, img_h = img.size
+ boxes: List[LayoutBox] = []
+ dropped_blank = 0
+ for blk in parsed:
+ canon = LAYOUT_PRED_RELABEL.get(blk.label, blk.label)
+ # Drop text-labeled blocks the model hallucinated over an
+ # essentially-blank region (mostly white OR near-uniform
+ # color). Visual blocks (Picture / Figure / Table / etc.)
+ # are allowed to be uniform — that's normal content.
+ if canon in TEXT_LABELS:
+ img_bbox = denorm_bbox(
+ blk.bbox, img_w, img_h, scale=settings.BBOX_SCALE
+ )
+ x0, y0, x1, y1 = (max(0, int(v)) for v in img_bbox)
+ if x1 > x0 and y1 > y0:
+ if is_blank_region(img.crop((x0, y0, x1, y1))):
+ dropped_blank += 1
+ continue
+ pixel_bbox = denorm_bbox(blk.bbox, w, h, scale=settings.BBOX_SCALE)
+ boxes.append(
LayoutBox(
- polygon=poly.tolist(),
- label=label,
- position=z,
- top_k=top_k_dict,
- confidence=score,
+ polygon=list(pixel_bbox),
+ label=canon,
+ raw_label=blk.label,
+ position=len(boxes),
+ count=blk.count,
+ confidence=confidence,
)
)
- layout_boxes = clean_boxes(layout_boxes)
- layout_results.append(
+ if dropped_blank:
+ logger.info(
+ f"dropped {dropped_blank} text-labeled layout block(s) over "
+ f"blank/uniform regions"
+ )
+ results.append(
LayoutResult(
- bboxes=layout_boxes,
- image_bbox=[0, 0, image.shape[1], image.shape[0]],
- ) # Image is numpy array
+ bboxes=boxes, image_bbox=page_bbox, raw=out.raw, error=False
+ )
)
-
- assert len(layout_results) == len(images)
- return layout_results
+ return results
diff --git a/surya/layout/label.py b/surya/layout/label.py
index 2b00599c..bb4c6e32 100644
--- a/surya/layout/label.py
+++ b/surya/layout/label.py
@@ -1,17 +1,44 @@
+"""Surya2 layout labels emitted by the model + canonicalization to surya's
+public label vocabulary."""
+
+# Canonical text-bearing labels — used by blank-region filters to decide
+# which blocks may be dropped when their underlying image region is empty.
+# Excludes Picture/Figure/Diagram/Table/Form/Equation/etc., which can legitimately
+# contain whitespace or solid fills.
+TEXT_LABELS = frozenset(
+ {
+ "Text",
+ "SectionHeader",
+ "PageHeader",
+ "PageFooter",
+ "Caption",
+ "Footnote",
+ "Code",
+ "Bibliography",
+ }
+)
+
+
+# Canonicalize raw model labels to public surya label names. Marker and other
+# downstream consumers depend on these names.
LAYOUT_PRED_RELABEL = {
- "": "PageHeader",
- "": "PageFooter",
- "": "Footnote",
- "": "Picture",
- "": "Figure",
- "": "Text",
- "": "Caption",
- "": "ListItem",
- "": "SectionHeader",
- "": "Table",
- "": "TableOfContents",
- "