diff --git a/.gitattributes b/.gitattributes old mode 100644 new mode 100755 index a6344aac8c09253b3b630fb776ae94478aa0275b..dab9a4e17afd2ef39d90ccb0b40ef2786fe77422 --- a/.gitattributes +++ b/.gitattributes @@ -1,35 +1,35 @@ -*.7z filter=lfs diff=lfs merge=lfs -text -*.arrow filter=lfs diff=lfs merge=lfs -text -*.bin filter=lfs diff=lfs merge=lfs -text -*.bz2 filter=lfs diff=lfs merge=lfs -text -*.ckpt filter=lfs diff=lfs merge=lfs -text -*.ftz filter=lfs diff=lfs merge=lfs -text -*.gz filter=lfs diff=lfs merge=lfs -text -*.h5 filter=lfs diff=lfs merge=lfs -text -*.joblib filter=lfs diff=lfs merge=lfs -text -*.lfs.* filter=lfs diff=lfs merge=lfs -text -*.mlmodel filter=lfs diff=lfs merge=lfs -text -*.model filter=lfs diff=lfs merge=lfs -text -*.msgpack filter=lfs diff=lfs merge=lfs -text -*.npy filter=lfs diff=lfs merge=lfs -text -*.npz filter=lfs diff=lfs merge=lfs -text -*.onnx filter=lfs diff=lfs merge=lfs -text -*.ot filter=lfs diff=lfs merge=lfs -text -*.parquet filter=lfs diff=lfs merge=lfs -text -*.pb filter=lfs diff=lfs merge=lfs -text -*.pickle filter=lfs diff=lfs merge=lfs -text -*.pkl filter=lfs diff=lfs merge=lfs -text -*.pt filter=lfs diff=lfs merge=lfs -text -*.pth filter=lfs diff=lfs merge=lfs -text -*.rar filter=lfs diff=lfs merge=lfs -text -*.safetensors filter=lfs diff=lfs merge=lfs -text -saved_model/**/* filter=lfs diff=lfs merge=lfs -text -*.tar.* filter=lfs diff=lfs merge=lfs -text -*.tar filter=lfs diff=lfs merge=lfs -text -*.tflite filter=lfs diff=lfs merge=lfs -text -*.tgz filter=lfs diff=lfs merge=lfs -text -*.wasm filter=lfs diff=lfs merge=lfs -text -*.xz filter=lfs diff=lfs merge=lfs -text -*.zip filter=lfs diff=lfs merge=lfs -text -*.zst filter=lfs diff=lfs merge=lfs -text -*tfevents* filter=lfs diff=lfs merge=lfs -text +*.7z filter=lfs diff=lfs merge=lfs -text +*.arrow filter=lfs diff=lfs merge=lfs -text +*.bin filter=lfs diff=lfs merge=lfs -text +*.bz2 filter=lfs diff=lfs merge=lfs -text +*.ckpt filter=lfs diff=lfs merge=lfs -text +*.ftz filter=lfs diff=lfs merge=lfs -text +*.gz filter=lfs diff=lfs merge=lfs -text +*.h5 filter=lfs diff=lfs merge=lfs -text +*.joblib filter=lfs diff=lfs merge=lfs -text +*.lfs.* filter=lfs diff=lfs merge=lfs -text +*.mlmodel filter=lfs diff=lfs merge=lfs -text +*.model filter=lfs diff=lfs merge=lfs -text +*.msgpack filter=lfs diff=lfs merge=lfs -text +*.npy filter=lfs diff=lfs merge=lfs -text +*.npz filter=lfs diff=lfs merge=lfs -text +*.onnx filter=lfs diff=lfs merge=lfs -text +*.ot filter=lfs diff=lfs merge=lfs -text +*.parquet filter=lfs diff=lfs merge=lfs -text +*.pb filter=lfs diff=lfs merge=lfs -text +*.pickle filter=lfs diff=lfs merge=lfs -text +*.pkl filter=lfs diff=lfs merge=lfs -text +*.pt filter=lfs diff=lfs merge=lfs -text +*.pth filter=lfs diff=lfs merge=lfs -text +*.rar filter=lfs diff=lfs merge=lfs -text +*.safetensors filter=lfs diff=lfs merge=lfs -text +saved_model/**/* filter=lfs diff=lfs merge=lfs -text +*.tar.* filter=lfs diff=lfs merge=lfs -text +*.tar filter=lfs diff=lfs merge=lfs -text +*.tflite filter=lfs diff=lfs merge=lfs -text +*.tgz filter=lfs diff=lfs merge=lfs -text +*.wasm filter=lfs diff=lfs merge=lfs -text +*.xz filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text +*.zst filter=lfs diff=lfs merge=lfs -text +*tfevents* filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 index aaa9ffd09c33fd23389f3073720ca621940bcdc6..b694934fbf9b49ee808b6dfc7292c28e2c46a97e --- a/.gitignore +++ b/.gitignore @@ -1,303 +1 @@ -### VisualStudioCode template -.vscode/ - -### JetBrains template -.idea/ - -### Linux template -*~ - -# temporary files which can be created if a process still has a handle open of a deleted file -.fuse_hidden* - -# KDE directory preferences -.directory - -# Linux trash folder which might appear on any partition or disk -.Trash-* - -# .nfs files are created when an open file is removed but is still being accessed -.nfs* - -### Windows template -# Windows thumbnail cache files -Thumbs.db -Thumbs.db:encryptable -ehthumbs.db -ehthumbs_vista.db - -# Dump file -*.stackdump - -# Folder config file -[Dd]esktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msix -*.msm -*.msp - -# Windows shortcuts -*.lnk - -### Archives template -# It's better to unpack these files and commit the raw source because -# git has its own built in compression methods. -*.7z -*.jar -*.rar -*.zip -*.gz -*.gzip -*.tgz -*.bzip -*.bzip2 -*.bz2 -*.xz -*.lzma -*.xar - -# Packing-only formats -*.iso -*.tar - -# Package management formats -*.dmg -*.xpi -*.gem -*.egg -*.deb -*.rpm -*.txz - -### JupyterNotebooks template -# gitignore template for Jupyter Notebooks -# website: http://jupyter.org/ - -.ipynb_checkpoints -*/.ipynb_checkpoints/* - -# IPython -profile_default/ -ipython_config.py - -# Remove previous ipynb_checkpoints -# git rm -r .ipynb_checkpoints/ - -### macOS template -# General -.DS_Store -.AppleDouble -.LSOverride - -# Icon must end with two \r -Icon - -# Thumbnails -._* - -# Files that might appear in the root of a volume -.DocumentRevisions-V100 -.fseventsd -.Spotlight-V100 -.TemporaryItems -.Trashes -.VolumeIcon.icns -.com.apple.timemachine.donotpresent - -# Directories potentially created on remote AFP share -.AppleDB -.AppleDesktop -Network Trash Folder -Temporary Items -.apdisk - -### Python template -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook - -# IPython - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -.python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm.fming.dev/#use-with-ide -.pdm.toml - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -### SublimeText template -# Cache files for Sublime Text -*.tmlanguage.cache -*.tmPreferences.cache -*.stTheme.cache - -# Workspace files are user-specific -*.sublime-workspace - -# Project files should be checked into the repository, unless a significant -# proportion of contributors will probably not be using Sublime Text -# *.sublime-project - -# SFTP configuration file -sftp-config.json -sftp-config-alt*.json - -# Package control specific files -Package Control.last-run -Package Control.ca-list -Package Control.ca-bundle -Package Control.system-ca-bundle -Package Control.cache/ -Package Control.ca-certs/ -Package Control.merged-ca-bundle -Package Control.user-ca-bundle -oscrypto-ca-bundle.crt -bh_unicode_properties.cache - -# Sublime-github package stores a github token in this file -# https://packagecontrol.io/packages/sublime-github -GitHub.sublime-settings - +.venv \ No newline at end of file diff --git a/.python-version b/.python-version new file mode 100644 index 0000000000000000000000000000000000000000..e4fba2183587225f216eeada4c78dfab6b2e65f5 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 7d4f96c54aa155a17387e64501c50b6a0469932c..0000000000000000000000000000000000000000 --- a/LICENSE +++ /dev/null @@ -1,427 +0,0 @@ -Attribution-ShareAlike 4.0 International - -======================================================================= - -Creative Commons Corporation ("Creative Commons") is not a law firm and -does not provide legal services or legal advice. Distribution of -Creative Commons public licenses does not create a lawyer-client or -other relationship. Creative Commons makes its licenses and related -information available on an "as-is" basis. Creative Commons gives no -warranties regarding its licenses, any material licensed under their -terms and conditions, or any related information. Creative Commons -disclaims all liability for damages resulting from their use to the -fullest extent possible. - -Using Creative Commons Public Licenses - -Creative Commons public licenses provide a standard set of terms and -conditions that creators and other rights holders may use to share -original works of authorship and other material subject to copyright -and certain other rights specified in the public license below. The -following considerations are for informational purposes only, are not -exhaustive, and do not form part of our licenses. - - Considerations for licensors: Our public licenses are - intended for use by those authorized to give the public - permission to use material in ways otherwise restricted by - copyright and certain other rights. Our licenses are - irrevocable. Licensors should read and understand the terms - and conditions of the license they choose before applying it. - Licensors should also secure all rights necessary before - applying our licenses so that the public can reuse the - material as expected. Licensors should clearly mark any - material not subject to the license. This includes other CC- - licensed material, or material used under an exception or - limitation to copyright. More considerations for licensors: - wiki.creativecommons.org/Considerations_for_licensors - - Considerations for the public: By using one of our public - licenses, a licensor grants the public permission to use the - licensed material under specified terms and conditions. If - the licensor's permission is not necessary for any reason--for - example, because of any applicable exception or limitation to - copyright--then that use is not regulated by the license. Our - licenses grant only permissions under copyright and certain - other rights that a licensor has authority to grant. Use of - the licensed material may still be restricted for other - reasons, including because others have copyright or other - rights in the material. A licensor may make special requests, - such as asking that all changes be marked or described. - Although not required by our licenses, you are encouraged to - respect those requests where reasonable. More considerations - for the public: - wiki.creativecommons.org/Considerations_for_licensees - -======================================================================= - -Creative Commons Attribution-ShareAlike 4.0 International Public -License - -By exercising the Licensed Rights (defined below), You accept and agree -to be bound by the terms and conditions of this Creative Commons -Attribution-ShareAlike 4.0 International Public License ("Public -License"). To the extent this Public License may be interpreted as a -contract, You are granted the Licensed Rights in consideration of Your -acceptance of these terms and conditions, and the Licensor grants You -such rights in consideration of benefits the Licensor receives from -making the Licensed Material available under these terms and -conditions. - - -Section 1 -- Definitions. - - a. Adapted Material means material subject to Copyright and Similar - Rights that is derived from or based upon the Licensed Material - and in which the Licensed Material is translated, altered, - arranged, transformed, or otherwise modified in a manner requiring - permission under the Copyright and Similar Rights held by the - Licensor. For purposes of this Public License, where the Licensed - Material is a musical work, performance, or sound recording, - Adapted Material is always produced where the Licensed Material is - synched in timed relation with a moving image. - - b. Adapter's License means the license You apply to Your Copyright - and Similar Rights in Your contributions to Adapted Material in - accordance with the terms and conditions of this Public License. - - c. BY-SA Compatible License means a license listed at - creativecommons.org/compatiblelicenses, approved by Creative - Commons as essentially the equivalent of this Public License. - - d. Copyright and Similar Rights means copyright and/or similar rights - closely related to copyright including, without limitation, - performance, broadcast, sound recording, and Sui Generis Database - Rights, without regard to how the rights are labeled or - categorized. For purposes of this Public License, the rights - specified in Section 2(b)(1)-(2) are not Copyright and Similar - Rights. - - e. Effective Technological Measures means those measures that, in the - absence of proper authority, may not be circumvented under laws - fulfilling obligations under Article 11 of the WIPO Copyright - Treaty adopted on December 20, 1996, and/or similar international - agreements. - - f. Exceptions and Limitations means fair use, fair dealing, and/or - any other exception or limitation to Copyright and Similar Rights - that applies to Your use of the Licensed Material. - - g. License Elements means the license attributes listed in the name - of a Creative Commons Public License. The License Elements of this - Public License are Attribution and ShareAlike. - - h. Licensed Material means the artistic or literary work, database, - or other material to which the Licensor applied this Public - License. - - i. Licensed Rights means the rights granted to You subject to the - terms and conditions of this Public License, which are limited to - all Copyright and Similar Rights that apply to Your use of the - Licensed Material and that the Licensor has authority to license. - - j. Licensor means the individual(s) or entity(ies) granting rights - under this Public License. - - k. Share means to provide material to the public by any means or - process that requires permission under the Licensed Rights, such - as reproduction, public display, public performance, distribution, - dissemination, communication, or importation, and to make material - available to the public including in ways that members of the - public may access the material from a place and at a time - individually chosen by them. - - l. Sui Generis Database Rights means rights other than copyright - resulting from Directive 96/9/EC of the European Parliament and of - the Council of 11 March 1996 on the legal protection of databases, - as amended and/or succeeded, as well as other essentially - equivalent rights anywhere in the world. - - m. You means the individual or entity exercising the Licensed Rights - under this Public License. Your has a corresponding meaning. - - -Section 2 -- Scope. - - a. License grant. - - 1. Subject to the terms and conditions of this Public License, - the Licensor hereby grants You a worldwide, royalty-free, - non-sublicensable, non-exclusive, irrevocable license to - exercise the Licensed Rights in the Licensed Material to: - - a. reproduce and Share the Licensed Material, in whole or - in part; and - - b. produce, reproduce, and Share Adapted Material. - - 2. Exceptions and Limitations. For the avoidance of doubt, where - Exceptions and Limitations apply to Your use, this Public - License does not apply, and You do not need to comply with - its terms and conditions. - - 3. Term. The term of this Public License is specified in Section - 6(a). - - 4. Media and formats; technical modifications allowed. The - Licensor authorizes You to exercise the Licensed Rights in - all media and formats whether now known or hereafter created, - and to make technical modifications necessary to do so. The - Licensor waives and/or agrees not to assert any right or - authority to forbid You from making technical modifications - necessary to exercise the Licensed Rights, including - technical modifications necessary to circumvent Effective - Technological Measures. For purposes of this Public License, - simply making modifications authorized by this Section 2(a) - (4) never produces Adapted Material. - - 5. Downstream recipients. - - a. Offer from the Licensor -- Licensed Material. Every - recipient of the Licensed Material automatically - receives an offer from the Licensor to exercise the - Licensed Rights under the terms and conditions of this - Public License. - - b. Additional offer from the Licensor -- Adapted Material. - Every recipient of Adapted Material from You - automatically receives an offer from the Licensor to - exercise the Licensed Rights in the Adapted Material - under the conditions of the Adapter's License You apply. - - c. No downstream restrictions. You may not offer or impose - any additional or different terms or conditions on, or - apply any Effective Technological Measures to, the - Licensed Material if doing so restricts exercise of the - Licensed Rights by any recipient of the Licensed - Material. - - 6. No endorsement. Nothing in this Public License constitutes or - may be construed as permission to assert or imply that You - are, or that Your use of the Licensed Material is, connected - with, or sponsored, endorsed, or granted official status by, - the Licensor or others designated to receive attribution as - provided in Section 3(a)(1)(A)(i). - - b. Other rights. - - 1. Moral rights, such as the right of integrity, are not - licensed under this Public License, nor are publicity, - privacy, and/or other similar personality rights; however, to - the extent possible, the Licensor waives and/or agrees not to - assert any such rights held by the Licensor to the limited - extent necessary to allow You to exercise the Licensed - Rights, but not otherwise. - - 2. Patent and trademark rights are not licensed under this - Public License. - - 3. To the extent possible, the Licensor waives any right to - collect royalties from You for the exercise of the Licensed - Rights, whether directly or through a collecting society - under any voluntary or waivable statutory or compulsory - licensing scheme. In all other cases the Licensor expressly - reserves any right to collect such royalties. - - -Section 3 -- License Conditions. - -Your exercise of the Licensed Rights is expressly made subject to the -following conditions. - - a. Attribution. - - 1. If You Share the Licensed Material (including in modified - form), You must: - - a. retain the following if it is supplied by the Licensor - with the Licensed Material: - - i. identification of the creator(s) of the Licensed - Material and any others designated to receive - attribution, in any reasonable manner requested by - the Licensor (including by pseudonym if - designated); - - ii. a copyright notice; - - iii. a notice that refers to this Public License; - - iv. a notice that refers to the disclaimer of - warranties; - - v. a URI or hyperlink to the Licensed Material to the - extent reasonably practicable; - - b. indicate if You modified the Licensed Material and - retain an indication of any previous modifications; and - - c. indicate the Licensed Material is licensed under this - Public License, and include the text of, or the URI or - hyperlink to, this Public License. - - 2. You may satisfy the conditions in Section 3(a)(1) in any - reasonable manner based on the medium, means, and context in - which You Share the Licensed Material. For example, it may be - reasonable to satisfy the conditions by providing a URI or - hyperlink to a resource that includes the required - information. - - 3. If requested by the Licensor, You must remove any of the - information required by Section 3(a)(1)(A) to the extent - reasonably practicable. - - b. ShareAlike. - - In addition to the conditions in Section 3(a), if You Share - Adapted Material You produce, the following conditions also apply. - - 1. The Adapter's License You apply must be a Creative Commons - license with the same License Elements, this version or - later, or a BY-SA Compatible License. - - 2. You must include the text of, or the URI or hyperlink to, the - Adapter's License You apply. You may satisfy this condition - in any reasonable manner based on the medium, means, and - context in which You Share Adapted Material. - - 3. You may not offer or impose any additional or different terms - or conditions on, or apply any Effective Technological - Measures to, Adapted Material that restrict exercise of the - rights granted under the Adapter's License You apply. - - -Section 4 -- Sui Generis Database Rights. - -Where the Licensed Rights include Sui Generis Database Rights that -apply to Your use of the Licensed Material: - - a. for the avoidance of doubt, Section 2(a)(1) grants You the right - to extract, reuse, reproduce, and Share all or a substantial - portion of the contents of the database; - - b. if You include all or a substantial portion of the database - contents in a database in which You have Sui Generis Database - Rights, then the database in which You have Sui Generis Database - Rights (but not its individual contents) is Adapted Material, - including for purposes of Section 3(b); and - - c. You must comply with the conditions in Section 3(a) if You Share - all or a substantial portion of the contents of the database. - -For the avoidance of doubt, this Section 4 supplements and does not -replace Your obligations under this Public License where the Licensed -Rights include other Copyright and Similar Rights. - - -Section 5 -- Disclaimer of Warranties and Limitation of Liability. - - a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE - EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS - AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF - ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, - IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, - WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR - PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, - ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT - KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT - ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. - - b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE - TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, - NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, - INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, - COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR - USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN - ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR - DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR - IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. - - c. The disclaimer of warranties and limitation of liability provided - above shall be interpreted in a manner that, to the extent - possible, most closely approximates an absolute disclaimer and - waiver of all liability. - - -Section 6 -- Term and Termination. - - a. This Public License applies for the term of the Copyright and - Similar Rights licensed here. However, if You fail to comply with - this Public License, then Your rights under this Public License - terminate automatically. - - b. Where Your right to use the Licensed Material has terminated under - Section 6(a), it reinstates: - - 1. automatically as of the date the violation is cured, provided - it is cured within 30 days of Your discovery of the - violation; or - - 2. upon express reinstatement by the Licensor. - - For the avoidance of doubt, this Section 6(b) does not affect any - right the Licensor may have to seek remedies for Your violations - of this Public License. - - c. For the avoidance of doubt, the Licensor may also offer the - Licensed Material under separate terms or conditions or stop - distributing the Licensed Material at any time; however, doing so - will not terminate this Public License. - - d. Sections 1, 5, 6, 7, and 8 survive termination of this Public - License. - - -Section 7 -- Other Terms and Conditions. - - a. The Licensor shall not be bound by any additional or different - terms or conditions communicated by You unless expressly agreed. - - b. Any arrangements, understandings, or agreements regarding the - Licensed Material not stated herein are separate from and - independent of the terms and conditions of this Public License. - - -Section 8 -- Interpretation. - - a. For the avoidance of doubt, this Public License does not, and - shall not be interpreted to, reduce, limit, restrict, or impose - conditions on any use of the Licensed Material that could lawfully - be made without permission under this Public License. - - b. To the extent possible, if any provision of this Public License is - deemed unenforceable, it shall be automatically reformed to the - minimum extent necessary to make it enforceable. If the provision - cannot be reformed, it shall be severed from this Public License - without affecting the enforceability of the remaining terms and - conditions. - - c. No term or condition of this Public License will be waived and no - failure to comply consented to unless expressly agreed to by the - Licensor. - - d. Nothing in this Public License constitutes or may be interpreted - as a limitation upon, or waiver of, any privileges and immunities - that apply to the Licensor or You, including from the legal - processes of any jurisdiction or authority. - - -======================================================================= - -Creative Commons is not a party to its public -licenses. Notwithstanding, Creative Commons may elect to apply one of -its public licenses to material it publishes and in those instances -will be considered the โ€œLicensor.โ€ The text of the Creative Commons -public licenses is dedicated to the public domain under the CC0 Public -Domain Dedication. Except for the limited purpose of indicating that -material is shared under a Creative Commons public license or as -otherwise permitted by the Creative Commons policies published at -creativecommons.org/policies, Creative Commons does not authorize the -use of the trademark "Creative Commons" or any other trademark or logo -of Creative Commons without its prior written consent including, -without limitation, in connection with any unauthorized modifications -to any of its public licenses or any other arrangements, -understandings, or agreements concerning use of licensed material. For -the avoidance of doubt, this paragraph does not form part of the -public licenses. - -Creative Commons may be contacted at creativecommons.org. diff --git a/Makefile b/Makefile deleted file mode 100644 index 09d6f70506c53081e55c32a5c345f1d541e97e8c..0000000000000000000000000000000000000000 --- a/Makefile +++ /dev/null @@ -1,6 +0,0 @@ - -install: - pip install -U -r requirements-dev.txt - -start: - gradio app.py diff --git a/README.md b/README.md index 5427953de4b92eabe45303406b9a3932ff3a50be..bdfe3630605575b5cc6c00a7a0363c06eed3051e 100644 --- a/README.md +++ b/README.md @@ -3,37 +3,9 @@ title: EcoLogits Calculator emoji: ๐Ÿงฎ colorFrom: green colorTo: indigo -sdk: gradio -sdk_version: 4.36.1 +sdk: streamlit +sdk_version: 1.40.2 app_file: app.py pinned: true license: cc-by-sa-4.0 ---- - -Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference - - -To do: - -- [ ] Add a tab with basic documentation - - [X] Definition of each indicator (check the use of tool tip in gradio ?) - - [ ] Basic explanation of the methodology - - Why it depends on the number of tokens? (What is a token?) - - What is taken into account? (usage, manufacturing for inference != training) - - What hypotheses have we made? - - [X] Explain how to reduce the impacts? What are good strategies? (Valentin) -- [ ] Add more indicators like (km by car, kg of concrete, ...) we can use [impactco2.fr](https://impactco2.fr/). (Valentin) - - [X] First version - - [ ] To enrich -- [ ] Call to actions: (Valentin) - - [X] Follow us on LinkedIn - - [ ] Share the results of a simulation (e.g. export an image generated with plotly for instance?) -- [X] Add an advanced/expert tab - - [X] True number of tokens - - [X] Expose more inputs like the electricity mix - - [X] Examples of electricity mixes - - [X] Custom number of parameters -- [x] Live reload mode -- [ ] Idea : "estimate a given prompt impact" function which allows to enter a prompt in a text field and estimate its impacts -- [ ] Idea: copy-paste a ChatGPT conversation link and estimate the impacts. -- [ ] Idea: compare with country electricity consumption +--- \ No newline at end of file diff --git a/__init__.py b/__init__.py new file mode 100755 index 0000000000000000000000000000000000000000..e53d4650102036d98cd320bda9d8e98feba875c8 --- /dev/null +++ b/__init__.py @@ -0,0 +1,8 @@ +from .content import * +from .constants import * +from .expert import expert_mode +from .utils import * +from .calculator import calculator_mode +from .impacts import get_impacts, display_impacts +from .models import load_models +from .electricity_mix import * \ No newline at end of file diff --git a/__pycache__/__init__.cpython-312.pyc b/__pycache__/__init__.cpython-312.pyc new file mode 100755 index 0000000000000000000000000000000000000000..db3d7e811a4254aa037ee03dc69bd2a888a9dec1 Binary files /dev/null and b/__pycache__/__init__.cpython-312.pyc differ diff --git a/__pycache__/calculator.cpython-312.pyc b/__pycache__/calculator.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f9cc04f3a0734e9849f08892726689222c2ca731 Binary files /dev/null and b/__pycache__/calculator.cpython-312.pyc differ diff --git a/__pycache__/constants.cpython-312.pyc b/__pycache__/constants.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..304b8c99abab83fec75a691afa83d98b4f77f975 Binary files /dev/null and b/__pycache__/constants.cpython-312.pyc differ diff --git a/__pycache__/content.cpython-312.pyc b/__pycache__/content.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4f7ab53a1154a01a29f2ee7cae8c9e09a268cf8a Binary files /dev/null and b/__pycache__/content.cpython-312.pyc differ diff --git a/__pycache__/electricity_mix.cpython-312.pyc b/__pycache__/electricity_mix.cpython-312.pyc new file mode 100755 index 0000000000000000000000000000000000000000..493ba36fa3fe1d97e6fc75d0a2c1e4ffea02cecd Binary files /dev/null and b/__pycache__/electricity_mix.cpython-312.pyc differ diff --git a/__pycache__/expert.cpython-312.pyc b/__pycache__/expert.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8449857a3944883dff891c5801c4852abd38e3c0 Binary files /dev/null and b/__pycache__/expert.cpython-312.pyc differ diff --git a/__pycache__/impacts.cpython-312.pyc b/__pycache__/impacts.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..347291646c274839c962a42825d6968643fdd555 Binary files /dev/null and b/__pycache__/impacts.cpython-312.pyc differ diff --git a/__pycache__/models.cpython-312.pyc b/__pycache__/models.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..54c92c64e4a3d8aef5901878d1897ec4e924222e Binary files /dev/null and b/__pycache__/models.cpython-312.pyc differ diff --git a/__pycache__/utils.cpython-312.pyc b/__pycache__/utils.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c56ecde4dee5977377b9b8bac677a2da48c2efbd Binary files /dev/null and b/__pycache__/utils.cpython-312.pyc differ diff --git a/app.py b/app.py old mode 100644 new mode 100755 index 23a954e370ae8d3c9e6415b099fe095aa41f4300..d540b73670dd57a479a9b87398384e5e4f8fbff6 --- a/app.py +++ b/app.py @@ -1,539 +1,46 @@ -import gradio as gr - -from ecologits.tracers.utils import llm_impacts, _avg -from ecologits.impacts.llm import compute_llm_impacts as compute_llm_impacts_expert -from ecologits.model_repository import models - -from src.assets import custom_css -from src.electricity_mix import COUNTRY_CODES, find_electricity_mix -from src.content import ( - HERO_TEXT, - ABOUT_TEXT, - CITATION_LABEL, - CITATION_TEXT, - LICENCE_TEXT, METHODOLOGY_TEXT -) -from src.constants import ( - PROVIDERS, - OPENAI_MODELS, - ANTHROPIC_MODELS, - COHERE_MODELS, - META_MODELS, - MISTRALAI_MODELS, - PROMPTS, - CLOSED_SOURCE_MODELS, - MODELS, -) -from src.utils import ( - format_impacts, - format_impacts_expert, - format_energy_eq_physical_activity, - PhysicalActivity, - format_energy_eq_electric_vehicle, - format_gwp_eq_streaming, - format_energy_eq_electricity_production, - EnergyProduction, - format_gwp_eq_airplane_paris_nyc, - format_energy_eq_electricity_consumption_ireland, - df_elec_mix_for_plot -) -from src.scrapper import process_input - -CUSTOM = "Custom" - -def model_list(provider: str) -> gr.Dropdown: - if provider == "openai": - return gr.Dropdown( - OPENAI_MODELS, - label="Model", - value=OPENAI_MODELS[0][1], - filterable=True, - ) - elif provider == "anthropic": - return gr.Dropdown( - ANTHROPIC_MODELS, - label="Model", - value=ANTHROPIC_MODELS[0][1], - filterable=True, - ) - elif provider == "cohere": - return gr.Dropdown( - COHERE_MODELS, - label="Model", - value=COHERE_MODELS[0][1], - filterable=True, - ) - elif provider == "huggingface_hub/meta": - return gr.Dropdown( - META_MODELS, - label="Model", - value=META_MODELS[0][1], - filterable=True, - ) - elif provider == "mistralai": - return gr.Dropdown( - MISTRALAI_MODELS, - label="Model", - value=MISTRALAI_MODELS[0][1], - filterable=True, - ) - - -def custom(): - return CUSTOM - -def model_active_params_fn(model_name: str, n_param: float): - if model_name == CUSTOM: - return n_param - provider, model_name = model_name.split('/', 1) - model = models.find_model(provider=provider, model_name=model_name) - try: #moe with range - return model.architecture.parameters.active.max - except: - try: #moe without range - return model.architecture.parameters.active - except: - try: #dense with range - return model.architecture.parameters.max - except: #dense without range - return model.architecture.parameters - -def model_total_params_fn(model_name: str, n_param: float): - if model_name == CUSTOM: - return n_param - provider, model_name = model_name.split('/', 1) - model = models.find_model(provider=provider, model_name=model_name) - try: #moe - return model.architecture.parameters.total.max - except: - try: #dense with range - return model.architecture.parameters.max - except: #dense without range - try: - return model.architecture.parameters.total - except: - return model.architecture.parameters - -def mix_fn(country_code: str, mix_adpe: float, mix_pe: float, mix_gwp: float): - if country_code == CUSTOM: - return mix_adpe, mix_pe, mix_gwp - return find_electricity_mix(country_code) - -with gr.Blocks(css=custom_css) as demo: - gr.Markdown(HERO_TEXT) - - with gr.Tab("๐Ÿงฎ Calculator"): - with gr.Row(): - gr.Markdown("# Estimate the environmental impacts of LLM inference") - with gr.Row(): - input_provider = gr.Dropdown( - PROVIDERS, - label="Provider", - value=PROVIDERS[0][1], - filterable=True, - ) - - input_model = gr.Dropdown( - OPENAI_MODELS, - label="Model", - value=OPENAI_MODELS[0][1], - filterable=True, - ) - input_provider.change(model_list, input_provider, input_model) - - input_prompt = gr.Dropdown( - PROMPTS, - label="Example prompt", - value=400, - ) - - - @gr.render(inputs=[input_provider, input_model, input_prompt]) - def render_simple(provider, model, prompt): - if provider.startswith("huggingface_hub"): - provider = provider.split("/")[0] - if models.find_model(provider, model) is not None: - impacts = llm_impacts( - provider=provider, - model_name=model, - output_token_count=prompt, - request_latency=100000 - ) - impacts = format_impacts(impacts) - - # Inference impacts - with gr.Blocks(): - if f"{provider}/{model}" in CLOSED_SOURCE_MODELS: - with gr.Row(): - gr.Markdown("""

โš ๏ธ You have selected a closed-source model. Please be aware that - some providers do not fully disclose information about such models. Consequently, our - estimates have a lower precision for closed-source models. For further details, refer to - our FAQ in the About section. -

""", elem_classes="warning-box") - - with gr.Row(): - gr.Markdown(""" - ## Environmental impacts - - To understand how the environmental impacts are computed go to the ๐Ÿ“– Methodology tab. - """) - with gr.Row(): - with gr.Column(scale=1, min_width=220): - gr.Markdown(f""" -

โšก๏ธ Energy

- $$ \Large {impacts.energy.magnitude:.3g} \ \large {impacts.energy.units} $$ -

Evaluates the electricity consumption


- """) - with gr.Column(scale=1, min_width=220): - gr.Markdown(f""" -

๐ŸŒ๏ธ GHG Emissions

- $$ \Large {impacts.gwp.magnitude:.3g} \ \large {impacts.gwp.units} $$ -

Evaluates the effect on global warming


- """) - with gr.Column(scale=1, min_width=220): - gr.Markdown(f""" -

๐Ÿชจ Abiotic Resources

- $$ \Large {impacts.adpe.magnitude:.3g} \ \large {impacts.adpe.units} $$ -

Evaluates the use of metals and minerals


- """) - with gr.Column(scale=1, min_width=220): - gr.Markdown(f""" -

โ›ฝ๏ธ Primary Energy

- $$ \Large {impacts.pe.magnitude:.3g} \ \large {impacts.pe.units} $$ -

Evaluates the use of energy resources


- """) - - # Impacts equivalents - with gr.Blocks(): - with gr.Row(): - gr.Markdown(""" - --- - ## That's equivalent to... - - Making this request to the LLM is equivalent to the following actions. - """) - with gr.Row(): - physical_activity, distance = format_energy_eq_physical_activity(impacts.energy) - if physical_activity == PhysicalActivity.WALKING: - physical_activity = "๐Ÿšถ " + physical_activity.capitalize() - if physical_activity == PhysicalActivity.RUNNING: - physical_activity = "๐Ÿƒ " + physical_activity.capitalize() - with gr.Column(scale=1, min_width=300): - gr.Markdown(f""" -

{physical_activity} $$ \Large {distance.magnitude:.3g}\ {distance.units} $$

-

Based on energy consumption


- """, latex_delimiters=[{"left": "$$", "right": "$$", "display": False}]) - - ev_eq = format_energy_eq_electric_vehicle(impacts.energy) - with gr.Column(scale=1, min_width=300): - gr.Markdown(f""" -

๐Ÿ”‹ Electric Vehicle $$ \Large {ev_eq.magnitude:.3g}\ {ev_eq.units} $$

-

Based on energy consumption


- """, latex_delimiters=[{"left": "$$", "right": "$$", "display": False}]) - - streaming_eq = format_gwp_eq_streaming(impacts.gwp) - with gr.Column(scale=1, min_width=300): - gr.Markdown(f""" -

โฏ๏ธ Streaming $$ \Large {streaming_eq.magnitude:.3g}\ {streaming_eq.units} $$

-

Based on GHG emissions


- """, latex_delimiters=[{"left": "$$", "right": "$$", "display": False}]) - - # Bigger scale impacts equivalent - with gr.Blocks(): - with gr.Row(): - gr.Markdown(""" - ## What if 1% of the planet does this request everyday for 1 year? - - If this use case is largely deployed around the world the equivalent impacts would be. (The - impacts of this request x 1% of 8 billion people x 365 days in a year.) - """) - with gr.Row(): - electricity_production, count = format_energy_eq_electricity_production(impacts.energy) - if electricity_production == EnergyProduction.NUCLEAR: - emoji = "โ˜ข๏ธ" - name = "Nuclear power plants" - if electricity_production == EnergyProduction.WIND: - emoji = "๐Ÿ’จ๏ธ " - name = "Wind turbines" - with gr.Column(scale=1, min_width=300): - gr.Markdown(f""" -

{emoji} $$ \Large {count.magnitude:.0f} $$ {name} (yearly)

-

Based on electricity consumption


- """, latex_delimiters=[{"left": "$$", "right": "$$", "display": False}]) - - ireland_count = format_energy_eq_electricity_consumption_ireland(impacts.energy) - with gr.Column(scale=1, min_width=300): - gr.Markdown(f""" -

๐Ÿ‡ฎ๐Ÿ‡ช $$ \Large {ireland_count.magnitude:.2g} $$ x Ireland (yearly โšก๏ธ cons.)

-

Based on electricity consumption


- """, latex_delimiters=[{"left": "$$", "right": "$$", "display": False}]) - - paris_nyc_airplane = format_gwp_eq_airplane_paris_nyc(impacts.gwp) - with gr.Column(scale=1, min_width=300): - gr.Markdown(f""" -

โœˆ๏ธ $$ \Large {paris_nyc_airplane.magnitude:,.0f} $$ Paris โ†” NYC

-

Based on GHG emissions


- """, latex_delimiters=[{"left": "$$", "right": "$$", "display": False}]) - - with gr.Tab("๐Ÿค“ Expert Mode"): - - with gr.Row(): - gr.Markdown("# ๐Ÿค“ Expert mode") - - model = gr.Dropdown( - MODELS + [CUSTOM], - label="Model name", - value="openai/gpt-3.5-turbo", - filterable=True, - interactive=True - ) - input_model_active_params = gr.Number( - label="Number of billions of active parameters", - value=45.0, - interactive=True - ) - input_model_total_params = gr.Number( - label="Number of billions of total parameters", - value=45.0, - interactive=True - ) - - model.change(fn=model_active_params_fn, - inputs=[model, input_model_active_params], - outputs=[input_model_active_params]) - model.change(fn=model_total_params_fn, - inputs=[model, input_model_total_params], - outputs=[input_model_total_params]) - input_model_active_params.input(fn=custom, outputs=[model]) - input_model_total_params.input(fn=custom, outputs=[model]) - - input_tokens = gr.Number( - label="Output tokens", - value=100 - ) - - mix = gr.Dropdown( - COUNTRY_CODES + [CUSTOM], - label="Location", - value="WOR", - filterable=True, - interactive=True - ) - input_mix_gwp = gr.Number( - label="Electricity mix - GHG emissions [kgCO2eq / kWh]", - value=find_electricity_mix('WOR')[2], - interactive=True - ) - input_mix_adpe = gr.Number( - label="Electricity mix - Abiotic resources [kgSbeq / kWh]", - value=find_electricity_mix('WOR')[0], - interactive=True - ) - input_mix_pe = gr.Number( - label="Electricity mix - Primary energy [MJ / kWh]", - value=find_electricity_mix('WOR')[1], - interactive=True - ) - - mix.change(fn=mix_fn, - inputs=[mix, input_mix_adpe, input_mix_pe, input_mix_gwp], - outputs=[input_mix_adpe, input_mix_pe, input_mix_gwp]) - input_mix_gwp.input(fn=custom, outputs=mix) - input_mix_adpe.input(fn=custom, outputs=mix) - input_mix_pe.input(fn=custom, outputs=mix) - - - @gr.render(inputs=[ - input_model_active_params, - input_model_total_params, - input_tokens, - input_mix_gwp, - input_mix_adpe, - input_mix_pe - ]) - def render_expert( - model_active_params, - model_total_params, - tokens, - mix_gwp, - mix_adpe, - mix_pe - ): - impacts = compute_llm_impacts_expert( - model_active_parameter_count=model_active_params, - model_total_parameter_count=model_total_params, - output_token_count=tokens, - request_latency=100000, - if_electricity_mix_gwp=mix_gwp, - if_electricity_mix_adpe=mix_adpe, - if_electricity_mix_pe=mix_pe - ) - impacts, usage, embodied = format_impacts_expert(impacts) - - with gr.Blocks(): - - with gr.Row(): - gr.Markdown(f""" -

Environmental impacts

- """) - - with gr.Row(): - with gr.Column(scale=1, min_width=220): - gr.Markdown(f""" -

โšก๏ธ Energy

- $$ \Large {impacts.energy.magnitude:.3g} \ \large {impacts.energy.units} $$ -

Evaluates the electricity consumption


- """) - - with gr.Column(scale=1, min_width=220): - gr.Markdown(f""" -

๐ŸŒ๏ธ GHG Emissions

- $$ \Large {impacts.gwp.magnitude:.3g} \ \large {impacts.gwp.units} $$ -

Evaluates the effect on global warming


- $$ \Large {100*usage.gwp.value / (usage.gwp.value + embodied.gwp.value):.3} $$ -

% of GWP by usage (vs embodied)


- """) - - with gr.Column(scale=1, min_width=220): - gr.Markdown(f""" -

๐Ÿชจ Abiotic Resources

- $$ \Large {impacts.adpe.magnitude:.3g} \ \large {impacts.adpe.units} $$ -

Evaluates the use of metals and minerals


- $$ \Large {100*usage.adpe.value / (usage.adpe.value + embodied.adpe.value):.3} $$ -

% of ADPE by usage (vs embodied)


- """) - - with gr.Column(scale=1, min_width=220): - gr.Markdown(f""" -

โ›ฝ๏ธ Primary Energy

- $$ \Large {impacts.pe.magnitude:.3g} \ \large {impacts.pe.units} $$ -

Evaluates the use of energy resources


- $$ \Large {100*usage.pe.value / (usage.pe.value + embodied.pe.value):.3} $$ -

% of PE by usage (vs embodied)


- """) - - with gr.Row(): - gr.Markdown(f""" -

How can location impact the footprint ?

- """) - - with gr.Row(): - gr.BarPlot(df_elec_mix_for_plot, - x='country', - y='electricity_mix', - sort='y', - scale=1, - height=250, - min_width=800, - x_title=None, - y_title='electricity mix in gCO2eq / kWh') - - # with gr.Tab("๐Ÿ” Evaluate your own usage"): - - # with gr.Row(): - # gr.Markdown(""" - # # ๐Ÿ” Evaluate your own usage - # โš ๏ธ For now, only ChatGPT conversation import is available. - # You can always try out other models - however results might be inaccurate due to fixed parameters, such as tokenization method. - # """) - - # def compute_own_impacts(amount_token, model): - # provider = model.split('/')[0].lower() - # model = model.split('/')[1] - # impacts = llm_impacts( - # provider=provider, - # model_name=model, - # output_token_count=amount_token, - # request_latency=100000 - # ) - - # impacts = format_impacts(impacts) - - # energy = f""" - #

โšก๏ธ Energy

- # $$ \Large {impacts.energy.magnitude:.3g} \ \large {impacts.energy.units} $$ - #

Evaluates the electricity consumption


- # """ - - # gwp = f""" - #

๐ŸŒ๏ธ GHG Emissions

- # $$ \Large {impacts.gwp.magnitude:.3g} \ \large {impacts.gwp.units} $$ - #

Evaluates the effect on global warming


- # """ - - # adp = f""" - #

๐Ÿชจ Abiotic Resources

- # $$ \Large {impacts.adpe.magnitude:.3g} \ \large {impacts.adpe.units} $$ - #

Evaluates the use of metals and minerals


- # """ - - # pe = f""" - #

โ›ฝ๏ธ Primary Energy

- # $$ \Large {impacts.pe.magnitude:.3g} \ \large {impacts.pe.units} $$ - #

Evaluates the use of energy resources


- # """ - - # return energy, gwp, adp, pe - - # def combined_function(text, model): - # n_token = process_input(text) - # energy, gwp, adp, pe = compute_own_impacts(n_token, model) - # return n_token, energy, gwp, adp, pe - - # with gr.Blocks(): - - # text_input = gr.Textbox(label="Paste the URL here (must be on https://chatgpt.com/share/xxxx format)") - # model = gr.Dropdown( - # MODELS, - # label="Model name", - # value="openai/gpt-4o", - # filterable=True, - # interactive=True - # ) - - # process_button = gr.Button("Estimate this usage footprint") - - # with gr.Accordion("โ„น๏ธ Infos", open=False): - # n_token = gr.Textbox(label="Total amount of tokens :") - - # with gr.Row(): - # with gr.Column(scale=1, min_width=220): - # energy = gr.Markdown() - # with gr.Column(scale=1, min_width=220): - # gwp = gr.Markdown() - # with gr.Column(scale=1, min_width=220): - # adp = gr.Markdown() - # with gr.Column(scale=1, min_width=220): - # pe = gr.Markdown() - - # process_button.click( - # fn=combined_function, - # inputs=[text_input, model], - # outputs=[n_token, energy, gwp, adp, pe] - # ) - - with gr.Tab("๐Ÿ“– Methodology"): - gr.Markdown(METHODOLOGY_TEXT, - elem_classes="descriptive-text", - latex_delimiters=[ - {"left": "$$", "right": "$$", "display": True}, - {"left": "$", "right": "$", "display": False} - ]) - - with gr.Tab("โ„น๏ธ About"): - gr.Markdown(ABOUT_TEXT, elem_classes="descriptive-text",) - - with gr.Accordion("๐Ÿ“š Citation", open=False): - gr.Textbox( - value=CITATION_TEXT, - label=CITATION_LABEL, - interactive=False, - show_copy_button=True, - lines=len(CITATION_TEXT.split('\n')), - ) - - # License - gr.Markdown(LICENCE_TEXT) - -if __name__ == '__main__': - demo.launch() +import streamlit as st + +from src.content import ( + HERO_TEXT, + ABOUT_TEXT, + CITATION_LABEL, + CITATION_TEXT, + LICENCE_TEXT, + INTRO_TEXT, + METHODOLOGY_TEXT +) + +from src.expert import expert_mode +from src.calculator import calculator_mode + +st.set_page_config(layout="wide", + page_title="ECOLOGITS", + page_icon='๐Ÿ’ฌ') + +st.html(HERO_TEXT) + +st.markdown(INTRO_TEXT) + +tab_calculator, tab_expert, tab_method, tab_about = st.tabs(['๐Ÿงฎ Calculator', '๐Ÿค“ Expert Mode', '๐Ÿ“– Methodology', 'โ„น๏ธ About']) + +with tab_calculator: + + calculator_mode() + +with tab_expert: + + expert_mode() + +with tab_method: + + st.write(METHODOLOGY_TEXT) + +with tab_about: + + st.write(ABOUT_TEXT) + +with st.expander('๐Ÿ“š Citation'): + st.html(CITATION_LABEL) + st.html(CITATION_TEXT) + +st.html(LICENCE_TEXT) \ No newline at end of file diff --git a/calculator.py b/calculator.py new file mode 100755 index 0000000000000000000000000000000000000000..ea3a8c29c27b1f6361cb6ac9ad292b540d8355c1 --- /dev/null +++ b/calculator.py @@ -0,0 +1,67 @@ +import streamlit as st +import pandas as pd + +from ecologits.tracers.utils import llm_impacts +from src.impacts import get_impacts, display_impacts, display_equivalent +from src.utils import format_impacts +from src.content import WARNING_CLOSED_SOURCE, WARNING_MULTI_MODAL, WARNING_BOTH +from src.models import load_models, clean_models_data + +from src.constants import PROMPTS + +def calculator_mode(): + + with st.container(border=True): + + df = load_models() + + col1, col2, col3 = st.columns(3) + + with col1: + provider = st.selectbox(label = 'Provider', + options = [x for x in df['provider_clean'].unique()], + index = 9) + provider_raw = df[df['provider_clean'] == provider]['provider'].values[0] + + with col2: + model = st.selectbox('Model', [x for x in df['name_clean'].unique() if x in df[df['provider_clean'] == provider]['name_clean'].unique()]) + model_raw = df[(df['provider_clean'] == provider) & (df['name_clean'] == model)]['name'].values[0] + + with col3: + output_tokens = st.selectbox('Example prompt', [x[0] for x in PROMPTS]) + + # WARNING DISPLAY + + df_filtered = df[(df['provider_clean'] == provider) & (df['name_clean'] == model)] + + if df_filtered['warning_arch'].values[0] and not df_filtered['warning_multi_modal'].values[0]: + st.warning(WARNING_CLOSED_SOURCE) + if df_filtered['warning_multi_modal'].values[0] and not df_filtered['warning_arch'].values[0]: + st.warning(WARNING_MULTI_MODAL) + if df_filtered['warning_arch'].values[0] and df_filtered['warning_multi_modal'].values[0]: + st.warning(WARNING_BOTH) + + try: + impacts = llm_impacts( + provider=provider_raw, + model_name=model_raw, + output_token_count=[x[1] for x in PROMPTS if x[0] == output_tokens][0], + request_latency=100000 + ) + + impacts, _, _ = format_impacts(impacts) + + with st.container(border=True): + + st.markdown('

Environmental impacts

', unsafe_allow_html=True) + st.markdown('

To understand how the environmental impacts are computed go to the ๐Ÿ“– Methodology tab.

', unsafe_allow_html=True) + display_impacts(impacts) + + with st.container(border=True): + + st.markdown('

That\'s equivalent to ...

', unsafe_allow_html=True) + st.markdown('

Making this request to the LLM is equivalent to the following actions :

', unsafe_allow_html=True) + display_equivalent(impacts) + + except Exception as e: + st.error('Could not find the model in the repository. Please try another model.') \ No newline at end of file diff --git a/constants.py b/constants.py new file mode 100755 index 0000000000000000000000000000000000000000..01f5f17177c0e02cb2539be85e25ad6f1408e0a5 --- /dev/null +++ b/constants.py @@ -0,0 +1,106 @@ +PROVIDERS = [ + ("OpenAI", "openai"), + ("Anthropic", "anthropic"), + ("Cohere", "cohere"), + ("Meta", "huggingface_hub/meta"), + ("Mistral AI", "mistralai"), +] + +OPENAI_MODELS = [ + ("GPT-4o", "gpt-4o"), + ("GPT-4-Turbo", "gpt-4-turbo"), + ("GPT-4", "gpt-4"), + ("GPT-3.5-Turbo", "gpt-3.5-turbo"), +] + +ANTHROPIC_MODELS = [ + ("Claude 3 Opus", "claude-3-opus-20240229"), + ("Claude 3 Sonnet", "claude-3-sonnet-20240229"), + ("Claude 3 Haiku", "claude-3-haiku-20240307"), + ("Claude 2.1", "claude-2.1"), + ("Claude 2.0", "claude-2.0"), + ("Claude Instant 1.2", "claude-instant-1.2"), +] + +COHERE_MODELS = [ + ("Command Light", "command-light"), + ("Command", "command"), + ("Command R", "command-r"), + ("Command R+", "command-r-plus"), +] + +META_MODELS = [ + ("Llama 3 8B", "meta-llama/Meta-Llama-3-8B"), + ("Llama 3 70B", "meta-llama/Meta-Llama-3-70B"), + ("Llama 2 7B", "meta-llama/Llama-2-7b-hf"), + ("Llama 2 13B", "meta-llama/Llama-2-13b-hf"), + ("Llama 2 70B", "meta-llama/Llama-2-70b-hf"), +] + +MISTRALAI_MODELS = [ + ("Mistral 7B", "open-mistral-7b"), + ("Mixtral 8x7B", "open-mixtral-8x7b"), + ("Mixtral 8x22B", "open-mixtral-8x22b"), + ("Tiny", "mistral-tiny-2312"), + ("Small", "mistral-small-2402"), + ("Medium", "mistral-medium-2312"), + ("Large", "mistral-large-2402"), +] + +PROMPTS = [ + ("Write a Tweet", 50), + ("Write an email", 170), + ("Write an article summary", 250), + ("Small conversation with a chatbot", 400), + ("Write a report of 5 pages", 5000), + ("Write the code for this app", 10000) +] +PROMPTS = [(s + f" ({v} output tokens)", v) for (s, v) in PROMPTS] + +CLOSED_SOURCE_MODELS = { + "openai/gpt-4o", + "openai/gpt-4-turbo", + "openai/gpt-4", + "openai/gpt-3.5-turbo", + "anthropic/claude-3-opus-20240229", + "anthropic/claude-3-sonnet-20240229", + "anthropic/claude-3-haiku-20240307", + "anthropic/claude-2.1", + "anthropic/claude-2.0", + "anthropic/claude-instant-1.2", + "mistralai/mistral-tiny-2312", + "mistralai/mistral-small-2402", + "mistralai/mistral-medium-2312", + "mistralai/mistral-large-2402", +} + +MODELS = [ + ("OpenAI / GPT-4o", "openai/gpt-4o"), + ("OpenAI / GPT-4-Turbo", "openai/gpt-4-turbo"), + ("OpenAI / GPT-4", "openai/gpt-4"), + ("OpenAI / GPT-3.5-Turbo", "openai/gpt-3.5-turbo"), + ("Anthropic / Claude 3 Opus", "anthropic/claude-3-opus-20240229"), + ("Anthropic / Claude 3 Sonnet", "anthropic/claude-3-sonnet-20240229"), + ("Anthropic / Claude 3 Haiku", "anthropic/claude-3-haiku-20240307"), + ("Anthropic / Claude 2.1", "anthropic/claude-2.1"), + ("Anthropic / Claude 2.0", "anthropic/claude-2.0"), + ("Anthropic / Claude Instant 1.2", "anthropic/claude-instant-1.2"), + ("Mistral AI / Mistral 7B", "mistralai/open-mistral-7b"), + ("Mistral AI / Mixtral 8x7B", "mistralai/open-mixtral-8x7b"), + ("Mistral AI / Mixtral 8x22B", "mistralai/open-mixtral-8x22b"), + ("Mistral AI / Tiny", "mistralai/mistral-tiny-2312"), + ("Mistral AI / Small", "mistralai/mistral-small-2402"), + ("Mistral AI / Medium", "mistralai/mistral-medium-2312"), + ("Mistral AI / Large", "mistralai/mistral-large-2402"), + ("Meta / Llama 3 8B", "huggingface_hub/meta-llama/Meta-Llama-3-8B"), + ("Meta / Llama 3 70B", "huggingface_hub/meta-llama/Meta-Llama-3-70B"), + ("Meta / Llama 2 7B", "huggingface_hub/meta-llama/Llama-2-7b-hf"), + ("Meta / Llama 2 13B", "huggingface_hub/meta-llama/Llama-2-13b-hf"), + ("Meta / Llama 2 70B", "huggingface_hub/meta-llama/Llama-2-70b-hf"), + ("Cohere / Command Light", "cohere/command-light"), + ("Cohere / Command", "cohere/command"), + ("Cohere / Command R", "cohere/command-r"), + ("Cohere / Command R+", "cohere/command-r-plus"), +] + +MODEL_REPOSITORY_URL = "https://raw.githubusercontent.com/genai-impact/ecologits/refs/heads/main/ecologits/data/models.json" \ No newline at end of file diff --git a/content.py b/content.py new file mode 100755 index 0000000000000000000000000000000000000000..e1503dc72bcbe2c3cd89a41479da761ff41996d5 --- /dev/null +++ b/content.py @@ -0,0 +1,232 @@ +HERO_TEXT = """ + +

๐Ÿงฎ EcoLogits Calculator

+
+

+ EcoLogits is a python library that tracks the energy consumption and environmental + footprint of using generative AI models through APIs. +

+
+
+""" + +INTRO_TEXT = """ +This tool is developed and maintained by [GenAI Impact](https://genai-impact.org/) non-profit. Learn more about +๐ŸŒฑ EcoLogits by reading the documentation on [ecologits.ai](https://ecologits.ai). + +๐Ÿฉท Support us by giving a โญ๏ธ on our [GitHub repository](https://github.com/genai-impact/ecologits) and by following our [LinkedIn page](https://www.linkedin.com/company/genai-impact/). +""" + +WARNING_CLOSED_SOURCE = """ +โš ๏ธ The model architecture has not been publicly released, expect lower precision of estimations. +""" + +WARNING_MULTI_MODAL = """ +โš ๏ธ The model architecture is multimodal, expect lower precision of estimations. +""" + +WARNING_BOTH = """ +โš ๏ธ The model architecture has not been publicly released and is multimodal, expect lower precision of estimations. +""" + +ABOUT_TEXT = r""" +## ๐ŸŽฏ Our goal +**The main goal of the EcoLogits Calculator is to raise awareness on the environmental impacts of LLM inference.** +The rapid evolution of generative AI is reshaping numerous industries and aspects of our daily lives. While these +advancements offer some benefits, they also **pose substantial environmental challenges that cannot be overlooked**. +Plus the issue of AI's environmental footprint has been mainly discussed at training stage but rarely at the inference +stage. That is an issue because **inference impacts for LLMs can largely overcome the training impacts when deployed +at large scales**. +At **[GenAI Impact](https://genai-impact.org/) we are dedicated to understanding and mitigating the environmental +impacts of generative AI** through rigorous research, innovative tools, and community engagement. Especially, in early +2024 we have launched an new open-source tool called [EcoLogits](https://github.com/genai-impact/ecologits) that tracks +the energy consumption and environmental footprint of using generative AI models through APIs. +## ๐Ÿ™‹ FAQ +**How we assess the impacts of closed-source models?** +Environmental impacts are calculated based on model architecture and parameter count. For closed-source models, we +lack transparency from providers, so we estimate parameter counts using available information. For GPT models, we +based our estimates on leaked GPT-4 architecture and scaled parameters count for GPT-4-Turbo and GPT-4o based on +pricing differences. For other proprietary models like Anthropic's Claude, we assume similar impacts for models +released around the same time with similar performance on public benchmarks. Please note that these estimates are +based on assumptions and may not be exact. Our methods are open-source and transparent so you can always see the +hypotheses we use. +**Which generative AI models or providers are supported?** +To see the full list of **generative AI providers** currently supported by EcoLogits, see the following +[documentation page](https://ecologits.ai/providers/). As of today we only support LLMs but we plan to add support for +embeddings, image generation, multi-modal models and more. If you are interested don't hesitate to +[join us](https://genai-impact.org/contact/) and accelerate our work! +**How to reduce AI environmental impacts?** +* Look at **indirect impacts** of your project. Does the finality of your project is impacting negatively the +environment? +* **Be frugal** and question your usage or need of AI + * Do you really need AI to solve your problem? + * Do you really need GenAI to solve your problem? (you can read this [paper](https://aclanthology.org/2023.emnlp-industry.39.pdf)) + * Use small and specialized models to solve your problem. + * Evaluate before, during and after the development of your project the environmental impacts with tools like + ๐ŸŒฑ [EcoLogits](https://github.com/genai-impact/ecologits) or [CodeCarbon](https://github.com/mlco2/codecarbon) + (see [more tools](https://github.com/samuelrince/awesome-green-ai)) + * Restrict the use case and limit the usage of your tool or feature to the desired purpose. +* Do NOT buy new GPUs / hardware + * Hardware manufacturing for data centers is around 50% of the impact. +* Use cloud instances that are located in low emissions / high energy efficiency data centers +(see [electricitymaps.com](https://app.electricitymaps.com/map)) +* Optimize your models for production + * Quantize your models. + * Use inference optimization tricks. + * Prefer fine-tuning of small and existing models over generalist models. +**What is the difference between **EcoLogits** and [CodeCarbon](https://github.com/mlco2/codecarbon)?** +EcoLogits is focused on estimating the environmental impacts of generative AI (only LLMs for now) used **through API +providers (such as OpenAI, Anthropic, Cloud APIs...)** whereas CodeCarbon is more general tool to measure energy +consumption and estimate GHG emissions measurement. If you deploy LLMs locally we encourage you to use CodeCarbon to +get real numbers of your energy consumption. +## ๐Ÿค— Contributing +We are eager to get feedback from the community, don't hesitate to engage the discussion with us on this +[GitHub thread](https://github.com/genai-impact/ecologits/discussions/45) or message us on +[LinkedIn](https://www.linkedin.com/company/genai-impact/). +We also welcome any open-source contributions on ๐ŸŒฑ **[EcoLogits](https://github.com/genai-impact/ecologits)** or on +๐Ÿงฎ **EcoLogits Calculator**. +## โš–๏ธ License +

+ This work is licensed under + + CC BY-SA 4.0 + + + + +

+## ๐Ÿ™Œ Acknowledgement +We thank [Data For Good](https://dataforgood.fr/) and [Boavizta](https://boavizta.org/en) for supporting the +development of this project. Their contributions of tools, best practices, and expertise in environmental impact +assessment have been invaluable. +We also extend our gratitude to the open-source contributions of ๐Ÿค— [Hugging Face](huggingface.com) on the LLM-Perf +Leaderboard. +## ๐Ÿค Contact +For general question on the project, please use the [GitHub thread](https://github.com/genai-impact/ecologits/discussions/45). +Otherwise use our contact form on [genai-impact.org/contact](https://genai-impact.org/contact/). +""" + + +METHODOLOGY_TEXT = r""" +## ๐Ÿ“– Methodology +We have developed a methodology to **estimate the energy consumption and environmental impacts for an LLM inference** +based on request parameters and hypotheses on the data center location, the hardware used, the model architecture and +more. +In this section we will only cover the principles of the methodology related to the ๐Ÿงฎ **EcoLogits Calculator**. If +you wish to learn more on the environmental impacts modeling of an LLM request checkout the +๐ŸŒฑ [EcoLogits documentation page](https://ecologits.ai/methodology/). +### Modeling impacts of an LLM request +The environmental impacts of an LLM inference are split into the **usage impacts** $I_{request}^u$ to account for +electricity consumption and the **embodied impacts** $I_{request}^e$ that relates to resource extraction, hardware +manufacturing and transportation. In general terms it can be expressed as follow: +$$ I_{request} = I_{request}^u + I_{request}^e $$ +$$ I_{request} = E_{request}*F_{em}+\frac{\Delta T}{\Delta L}*I_{server}^e $$ +With, +* $E_{request}$ the estimated energy consumption of the server and its cooling system. +* $F_{em}$ the electricity mix that depends on the country and time. +* $\frac{\Delta T}{\Delta L}$ the hardware usage ratio i.e. the computation time over the lifetime of the hardware. +* $I_{server}^e$ the embodied impacts of the server. +Additionally, to โšก๏ธ **direct energy consumption** the environmental impacts are expressed in **three dimensions +(multi-criteria impacts)** that are: +* ๐ŸŒ **Global Warming Potential** (GWP): Potential impact on global warming in kgCO2eq (commonly known as GHG/carbon +emissions). +* ๐Ÿชจ **Abiotic Depletion Potential for Elements** (ADPe): Impact on the depletion of non-living resources such as +minerals or metals in kgSbeq. +* โ›ฝ๏ธ **Primary Energy** (PE): Total energy consumed from primary sources in MJ. +### Principles, Data and Hypotheses +We use a **bottom-up methodology** to model impacts, meaning that we will estimate the impacts of low-level physical +components to then estimate the impacts at software level (in that case an LLM inference). We also rely on **Life +Cycle Approach (LCA) proxies and approach** to model both usage and embodied phases with multi-criteria impacts. +If you are interested in this approach we recommend you to read the following [Boavizta](https://boavizta.org/) +resources. +* [Digital & environment: How to evaluate server manufacturing footprint, beyond greenhouse gas emissions?](https://boavizta.org/en/blog/empreinte-de-la-fabrication-d-un-serveur) +* [Boavizta API automated evaluation of environmental impacts of ICT services and equipments](https://boavizta.org/en/blog/boavizta-api-automated-evaluation-of-ict-impacts-on-the-environment) +* [Boavizta API documentation](https://doc.api.boavizta.org/) +We leverage **open data to estimate the environmental impacts**, here is an exhaustive list of our data providers. +* [LLM-Perf Leaderboard](https://huggingface.co/spaces/optimum/llm-perf-leaderboard) to estimate GPU energy consumption +and latency based on the model architecture and number of output tokens. +* [Boavizta API](https://github.com/Boavizta/boaviztapi) to estimate server embodied impacts and base energy +consumption. +* [ADEME Base Empreinteยฎ](https://base-empreinte.ademe.fr/) for electricity mix impacts per country. +Finally here are the **main hypotheses** we have made to compute the impacts. +* โš ๏ธ **We *"guesstimate"* the model architecture of proprietary LLMs when not disclosed by the provider.** +* Production setup: quantized models running on data center grade servers and GPUs such as A100. +* Electricity mix does not depend on time (help us enhance EcoLogits and work on this [issue](https://github.com/genai-impact/ecologits/issues/42)) +* Ignore the following impacts: unused cloud resources, data center building, network and end-user devices... (for now) +## Equivalents +We have integrated impact equivalents to help people better understand the impacts and have reference points for +standard use cases and everyday activities. +### Request impacts +These equivalents are computed based on the request impacts only. +#### ๐Ÿšถโ€โ™‚๏ธโ€โžก๏ธ Walking or ๐Ÿƒโ€โ™‚๏ธโ€โžก๏ธ running distance +We compare the โšก๏ธ direct energy consumption with the energy consumption of someone ๐Ÿšถโ€โ™‚๏ธโ€โžก๏ธ walking or ๐Ÿƒโ€โ™‚๏ธโ€โžก๏ธ running. +From [runningtools.com](https://www.runningtools.com/energyusage.htm) we consider the following energy values per +physical activity (for someone weighing 70kg): +* ๐Ÿšถโ€โ™‚๏ธโ€โžก๏ธ walking: $ 196\ kJ/km $ (speed of $ 3\ km/h $) +* ๐Ÿƒโ€โ™‚๏ธโ€โžก๏ธ running: $ 294\ kJ/km $ (speed of $ 10\ km/h $) +We divide the request energy consumption by these values to compute the distance traveled. +#### ๐Ÿ”‹ Electric Vehicle distance +We compare the โšก๏ธ direct energy consumption with the energy consumer by a EV car. From +[selectra.info](https://selectra.info/energie/actualites/insolite/consommation-vehicules-electriques-france-2040) or +[tesla.com](https://www.tesla.com/fr_fr/support/power-consumption) we consider an average value of energy consumed per +kilometer of: $ 0.17\ kWh/km $. +We divide the request energy consumption by this value to compute the distance driven by an EV. +#### โฏ๏ธ Streaming time +We compare the ๐ŸŒ GHG emissions of the request and of streaming a video. From +[impactco2.fr](https://impactco2.fr/outils/comparateur?value=1&comparisons=streamingvideo), we consider that +$ 1\ kgCO2eq $ is equivalent to $ 15.6\ h $ of streaming. +We multiply that value by the GHG emissions of the request to get an equivalent in hours of video streaming. +### Scaled impacts +These equivalents are computed based on the request impacts scaled to a worldwide adoption use case. We imply that the +same request is done 1% of the planet everyday for 1 year, and then compute impact equivalents. +$$ +I_{scaled} = I_{request} * [1 \\% \ \text{of}\ 8B\ \text{people on earth}] * 365\ \text{days} +$$ +#### Number of ๐Ÿ’จ wind turbines or โ˜ข๏ธ nuclear plants +We compare the โšก๏ธ direct energy consumption (scaled) by the energy production of wind turbines and nuclear power +plants. From [ecologie.gouv.fr](https://www.ecologie.gouv.fr/eolien-terrestre) we consider that a $ 2\ MW $ wind +turbine produces $ 4.2\ GWh $ a year. And from [edf.fr](https://www.edf.fr/groupe-edf/espaces-dedies/jeunes-enseignants/pour-les-jeunes/lenergie-de-a-a-z/produire-de-lelectricite/le-nucleaire-en-chiffres) +we learn that a $ 900\ MW $ nuclear power plant produces $ 6\ TWh $ a year. +We divide the scaled energy consumption by these values to get the number of wind turbines or nuclear power plants +needed. +#### Multiplier of ๐Ÿ‡ฎ๐Ÿ‡ช Ireland electricity consumption +We compare the โšก๏ธ direct energy consumption (scaled) by the electricity consumption of Ireland per year. From +[wikipedia.org](https://en.wikipedia.org/wiki/List_of_countries_by_electricity_consumption) we consider the Ireland +electricity consumption to be $ 33\ TWh $ a year for a population of 5M. +We divide the scaled energy consumption by this value to get the equivalent number of "Ireland countries". +#### Number of โœˆ๏ธ Paris โ†” New York City flights +We compare the ๐ŸŒ GHG emissions (scaled) of the request and of a return flight Paris โ†” New York City. From +[impactco2.fr](https://impactco2.fr/outils/comparateur?value=1&comparisons=&equivalent=avion-pny) we consider that a +return flight Paris โ†’ New York City โ†’ Paris for one passenger emits $ 1,770\ kgCO2eq $ and we consider an overall +average load of 100 passengers per flight. +We divide the scaled GHG emissions by this value to get the equivalent number of return flights. + +#### If you are motivated to help us test and enhance this methodology [contact us](https://genai-impact.org/contact/)! ๐Ÿ’ช +""" + +CITATION_LABEL = "BibTeX citation for EcoLogits Calculator and the EcoLogits library:" +CITATION_TEXT = r"""@misc{ecologits-calculator, + author={Samuel Rincรฉ, Adrien Banse and Valentin Defour}, + title={EcoLogits Calculator}, + year={2025}, + howpublished= {\url{https://huggingface.co/spaces/genai-impact/ecologits-calculator}}, +} +@software{ecologits, + author = {Samuel Rincรฉ, Adrien Banse, Vinh Nguyen and Luc Berton}, + publisher = {GenAI Impact}, + title = {EcoLogits: track the energy consumption and environmental footprint of using generative AI models through APIs.}, +}""" + +LICENCE_TEXT = """

+ This work is licensed under + + CC BY-SA 4.0 + + + + +

""" \ No newline at end of file diff --git a/src/electricity_mix.csv b/data/electricity_mix.csv old mode 100644 new mode 100755 similarity index 99% rename from src/electricity_mix.csv rename to data/electricity_mix.csv index 8ddc81db67b6f7ba68b6f440fdbd2a7268d241c8..416ad2525308ff910b9d38621c2db7f2bb250158 --- a/src/electricity_mix.csv +++ b/data/electricity_mix.csv @@ -1,4 +1,4 @@ -name,unit,source,WOR,EEE,ZWE,ZMB,ZAF,YEM,VNM,VEN,UZB,URY,USA,UKR,TZA,TWN,TTO,TUR,TUN,TKM,TJK,THA,TGO,SYR,SLV,SEN,SVK,SVN,SGP,SWE,SDN,SAU,RUS,SCG,ROU,QAT,PRY,PRT,POL,PAK,PHL,PER,PAN,OMN,NZL,NPL,NOR,NLD,NIC,NGA,NAM,MOZ,MYS,MEX,MLT,MNG,MMR,MKD,MDA,MAR,LBY,LVA,LUX,LTU,LKA,LBN,KAZ,KWT,KOR,PRK,KHM,KGZ,KEN,JPN,JOR,JAM,ITA,ISL,IRN,IRQ,IND,ISR,IRL,IDN,HUN,HTI,HRV,HND,HKG,GTM,GRC,GIB,GHA,GEO,GBR,GAB,FRA,FIN,ETH,ESP,ERI,EGY,EST,ECU,DZA,DOM,DNK,DEU,CZE,CYP,CUB,CRI,COL,CHN,CMR,CHL,CIV,CHE,COG,COD,CAN,BLR,BWA,BRA,BOL,BRN,BEN,BHR,BGR,BEL,BGD,BIH,AZE,AUS,AUT,ARG,AGO,ANT,ARM,ALB,ARE -adpe,kg รฉq. Sb,ADEME Base IMPACTS ยฎ,0.0000000737708,0.0000000642317,0.000000109502,0.000000162193,0.0000000862445,0.0000000163908,0.0000000945573,0.000000112811,0.000000103681,0.000000104586,0.0000000985548,0.0000000647907,0.000000132261,0.0000000578088,0.000000064552,0.0000000749765,0.0000000177021,0.000000131822,0.000000152621,0.0000000569593,0.000000134255,0.0000000268396,0.0000000472135,0.0000000470662,0.0000000606109,0.0000000992283,0.0000000198459,0.0000000777062,0.0000000955701,0.0000000134206,0.0000000960312,0.000000132772,0.0000000981761,0.00000001324,0.000000149181,0.0000000341863,0.000000101946,0.0000000842952,0.0000000595304,0.0000000952688,0.0000000790553,0.0000000374073,0.0000000720474,0.000000238273,0.000000127486,0.0000000329318,0.0000000414983,0.0000000621,0.000000128285,0.000000148382,0.000000044938,0.0000000578358,0.000000049475,0.000000176361,0.000000152699,0.000000119873,0.000000110674,0.0000000641089,0.0000000206592,0.000000153757,0.000000105692,0.0000000294596,0.0000000986932,0.0000000182134,0.000000135386,0.0000000141168,0.0000000518017,0.000000117457,0.0000000319202,0.000000181827,0.0000000958533,0.0000000596578,0.0000000147031,0.0000000196047,0.00000005439,0.0000000781905,0.0000000220304,0.0000000404306,0.000000100099,0.0000000610194,0.0000000219257,0.0000000610451,0.0000000644587,0.0000000937057,0.000000153989,0.0000000649373,0.0000000816213,0.0000000803251,0.0000000691645,0.0000000286211,0.000000156003,0.000000137999,0.0000000370973,0.000000113843,0.0000000485798,0.0000000805114,0.000000174161,0.0000000518326,0.0000000512406,0.000000033925,0.0000000990171,0.000000127168,0.0000000216438,0.0000000429285,0.0000000157411,0.0000000878733,0.0000000817565,0.0000000448771,0.0000000299542,0.0000000863908,0.000000122031,0.0000000851552,0.000000146313,0.000000105851,0.0000000949004,0.000000100467,0.000000265575,0.000000174647,0.0000000993179,0.0000000840478,0.0000000866014,0.00000010962,0.0000000969793,0.0000000185641,0.0000000239702,0.0000000135014,0.0000000823611,0.0000000337201,0.0000000394158,0.000000148007,0.000000092567,0.0000000790846,0.000000141124,0.0000000768612,0.000000124074,0.0000000449103,0.0000000854245,0.000000229556,0.0000000141548 -pe,MJ,ADPf / (1-%renewable_energy),9.988,12.873,16.122,1.798,11.732,16.250,11.238,15.163,17.367,107.570,11.358,12.936,9.305,11.439,14.289,16.150,12.902,23.300,19.165,10.414,21.978,16.989,13.012,14.516,11.680,12.146,10.477,11.026,29.629,14.058,13.200,14.242,15.585,11.916,0.020,14.153,13.178,16.175,11.120,8.211,16.364,22.306,24.731,0.396,4.952,8.511,24.696,11.279,468.150,0.206,12.268,11.775,19.374,15.114,14.132,19.120,18.429,11.702,19.116,8.249,10.128,21.043,12.116,12.341,13.260,12.753,10.199,32.793,34.655,15.380,68.996,10.718,13.677,14.799,12.656,0.013,15.022,20.372,20.363,10.023,10.706,11.603,11.784,20.167,18.548,15.762,,14.340,14.487,,10.097,10.425,13.579,28.341,11.289,11.275,36.133,12.090,13.289,10.195,16.334,20.908,16.376,12.412,16.824,16.260,12.517,13.118,17.317,45.996,7.312,14.119,10.807,11.348,14.783,11.782,34.147,0.097,11.987,13.194,19.642,9.031,11.587,15.689,14.337,14.036,14.375,10.776,12.935,21.705,12.831,16.908,11.036,10.049,16.972,,13.380,0.201,19.032 +name,unit,source,WOR,EEE,ZWE,ZMB,ZAF,YEM,VNM,VEN,UZB,URY,USA,UKR,TZA,TWN,TTO,TUR,TUN,TKM,TJK,THA,TGO,SYR,SLV,SEN,SVK,SVN,SGP,SWE,SDN,SAU,RUS,SCG,ROU,QAT,PRY,PRT,POL,PAK,PHL,PER,PAN,OMN,NZL,NPL,NOR,NLD,NIC,NGA,NAM,MOZ,MYS,MEX,MLT,MNG,MMR,MKD,MDA,MAR,LBY,LVA,LUX,LTU,LKA,LBN,KAZ,KWT,KOR,PRK,KHM,KGZ,KEN,JPN,JOR,JAM,ITA,ISL,IRN,IRQ,IND,ISR,IRL,IDN,HUN,HTI,HRV,HND,HKG,GTM,GRC,GIB,GHA,GEO,GBR,GAB,FRA,FIN,ETH,ESP,ERI,EGY,EST,ECU,DZA,DOM,DNK,DEU,CZE,CYP,CUB,CRI,COL,CHN,CMR,CHL,CIV,CHE,COG,COD,CAN,BLR,BWA,BRA,BOL,BRN,BEN,BHR,BGR,BEL,BGD,BIH,AZE,AUS,AUT,ARG,AGO,ANT,ARM,ALB,ARE +adpe,kg รฉq. Sb,ADEME Base IMPACTS ยฎ,0.0000000737708,0.0000000642317,0.000000109502,0.000000162193,0.0000000862445,0.0000000163908,0.0000000945573,0.000000112811,0.000000103681,0.000000104586,0.0000000985548,0.0000000647907,0.000000132261,0.0000000578088,0.000000064552,0.0000000749765,0.0000000177021,0.000000131822,0.000000152621,0.0000000569593,0.000000134255,0.0000000268396,0.0000000472135,0.0000000470662,0.0000000606109,0.0000000992283,0.0000000198459,0.0000000777062,0.0000000955701,0.0000000134206,0.0000000960312,0.000000132772,0.0000000981761,0.00000001324,0.000000149181,0.0000000341863,0.000000101946,0.0000000842952,0.0000000595304,0.0000000952688,0.0000000790553,0.0000000374073,0.0000000720474,0.000000238273,0.000000127486,0.0000000329318,0.0000000414983,0.0000000621,0.000000128285,0.000000148382,0.000000044938,0.0000000578358,0.000000049475,0.000000176361,0.000000152699,0.000000119873,0.000000110674,0.0000000641089,0.0000000206592,0.000000153757,0.000000105692,0.0000000294596,0.0000000986932,0.0000000182134,0.000000135386,0.0000000141168,0.0000000518017,0.000000117457,0.0000000319202,0.000000181827,0.0000000958533,0.0000000596578,0.0000000147031,0.0000000196047,0.00000005439,0.0000000781905,0.0000000220304,0.0000000404306,0.000000100099,0.0000000610194,0.0000000219257,0.0000000610451,0.0000000644587,0.0000000937057,0.000000153989,0.0000000649373,0.0000000816213,0.0000000803251,0.0000000691645,0.0000000286211,0.000000156003,0.000000137999,0.0000000370973,0.000000113843,0.0000000485798,0.0000000805114,0.000000174161,0.0000000518326,0.0000000512406,0.000000033925,0.0000000990171,0.000000127168,0.0000000216438,0.0000000429285,0.0000000157411,0.0000000878733,0.0000000817565,0.0000000448771,0.0000000299542,0.0000000863908,0.000000122031,0.0000000851552,0.000000146313,0.000000105851,0.0000000949004,0.000000100467,0.000000265575,0.000000174647,0.0000000993179,0.0000000840478,0.0000000866014,0.00000010962,0.0000000969793,0.0000000185641,0.0000000239702,0.0000000135014,0.0000000823611,0.0000000337201,0.0000000394158,0.000000148007,0.000000092567,0.0000000790846,0.000000141124,0.0000000768612,0.000000124074,0.0000000449103,0.0000000854245,0.000000229556,0.0000000141548 +pe,MJ,ADPf / (1-%renewable_energy),9.988,12.873,16.122,1.798,11.732,16.250,11.238,15.163,17.367,107.570,11.358,12.936,9.305,11.439,14.289,16.150,12.902,23.300,19.165,10.414,21.978,16.989,13.012,14.516,11.680,12.146,10.477,11.026,29.629,14.058,13.200,14.242,15.585,11.916,0.020,14.153,13.178,16.175,11.120,8.211,16.364,22.306,24.731,0.396,4.952,8.511,24.696,11.279,468.150,0.206,12.268,11.775,19.374,15.114,14.132,19.120,18.429,11.702,19.116,8.249,10.128,21.043,12.116,12.341,13.260,12.753,10.199,32.793,34.655,15.380,68.996,10.718,13.677,14.799,12.656,0.013,15.022,20.372,20.363,10.023,10.706,11.603,11.784,20.167,18.548,15.762,,14.340,14.487,,10.097,10.425,13.579,28.341,11.289,11.275,36.133,12.090,13.289,10.195,16.334,20.908,16.376,12.412,16.824,16.260,12.517,13.118,17.317,45.996,7.312,14.119,10.807,11.348,14.783,11.782,34.147,0.097,11.987,13.194,19.642,9.031,11.587,15.689,14.337,14.036,14.375,10.776,12.935,21.705,12.831,16.908,11.036,10.049,16.972,,13.380,0.201,19.032 gwp,kg รฉq. CO2,ADEME Base IMPACTS ยฎ,0.590478,0.509427,0.842811,0.0141304,1.17562,1.06777,0.555572,0.497373,0.81178,0.296953,0.67978,0.646745,0.475635,0.845351,0.933059,0.706988,0.80722,1.38296,0.0426743,0.646174,0.545455,1.08778,0.473128,1.1195,0.309341,0.498523,0.655825,0.0464664,1.12472,0.913677,0.66131,1.07808,0.664245,0.722125,0.241601,0.571172,1.15075,0.748727,0.761317,0.284364,0.53403,1.41292,0.293397,0.0841323,0.023754,0.544803,0.941626,0.693123,0.357253,0.00880732,0.832206,0.739214,1.31149,1.47192,0.48193,1.24074,1.04213,0.933694,1.35361,0.234273,0.490016,0.154229,0.709185,0.883627,1.128,0.885084,0.599585,0.797361,1.41054,0.156039,0.589603,0.540891,0.781372,1.07345,0.621329,0.0194609,0.930385,1.48728,1.58299,0.901842,0.648118,0.875394,0.541558,1.3858,0.535759,0.692837,0.95888,0.645801,1.13127,0.977477,0.540126,0.132046,0.602137,0.732511,0.0813225,0.322068,0.251299,0.467803,1.13153,0.587775,1.51492,0.627714,1.02318,0.909252,0.633534,0.657374,0.799077,0.978041,1.28325,0.224471,0.214014,1.05738,0.456622,0.574415,0.928583,0.0448568,0.904053,0.0613102,0.238191,0.795697,2.12572,0.278647,0.585131,0.997577,1.19128,0.804567,0.827087,0.256537,0.795168,1.56025,0.726126,1.12326,0.245573,0.476811,0.412014,1.13161,0.252002,0.0207635,1.09088 \ No newline at end of file diff --git a/electricity_mix.py b/electricity_mix.py new file mode 100755 index 0000000000000000000000000000000000000000..4395be484e7dbec8b723bb768e73c9e3acafa7fe --- /dev/null +++ b/electricity_mix.py @@ -0,0 +1,175 @@ +from csv import DictReader +import pandas as pd + +PATH = "src/data/electricity_mix.csv" + +COUNTRY_CODES = [ + ("๐ŸŒŽ World", "WOR"), + ("๐Ÿ‡ช๐Ÿ‡บ Europe", "EEE"), + ("๐Ÿ‡ฟ๐Ÿ‡ผ Zimbabwe", "ZWE"), + ("๐Ÿ‡ฟ๐Ÿ‡ฒ Zambia", "ZMB"), + ("๐Ÿ‡ฟ๐Ÿ‡ฆ South Africa", "ZAF"), + ("๐Ÿ‡พ๐Ÿ‡ช Yemen", "YEM"), + ("๐Ÿ‡ป๐Ÿ‡ณ Vietnam", "VNM"), + ("๐Ÿ‡ป๐Ÿ‡ช Venezuela", "VEN"), + ("๐Ÿ‡บ๐Ÿ‡ฟ Uzbekistan", "UZB"), + ("๐Ÿ‡บ๐Ÿ‡พ Uruguay", "URY"), + ("๐Ÿ‡บ๐Ÿ‡ธ United States", "USA"), + ("๐Ÿ‡บ๐Ÿ‡ฆ Ukraine", "UKR"), + ("๐Ÿ‡น๐Ÿ‡ฟ Tanzania", "TZA"), + ("๐Ÿ‡น๐Ÿ‡ผ Taiwan", "TWN"), + ("๐Ÿ‡น๐Ÿ‡น Trinidad and Tobago", "TTO"), + ("๐Ÿ‡น๐Ÿ‡ท Turkey", "TUR"), + ("๐Ÿ‡น๐Ÿ‡ณ Tunisia", "TUN"), + ("๐Ÿ‡น๐Ÿ‡ฒ Turkmenistan", "TKM"), + ("๐Ÿ‡น๐Ÿ‡ฏ Tajikistan", "TJK"), + ("๐Ÿ‡น๐Ÿ‡ญ Thailand", "THA"), + ("๐Ÿ‡น๐Ÿ‡ฌ Togo", "TGO"), + ("๐Ÿ‡ธ๐Ÿ‡พ Syrian Arab Republic", "SYR"), + ("๐Ÿ‡ธ๐Ÿ‡ป El Salvador", "SLV"), + ("๐Ÿ‡ธ๐Ÿ‡ณ Senegal", "SEN"), + ("๐Ÿ‡ธ๐Ÿ‡ฐ Slovak Republic", "SVK"), + ("๐Ÿ‡ธ๐Ÿ‡ฎ Slovenia", "SVN"), + ("๐Ÿ‡ธ๐Ÿ‡ฌ Singapore", "SGP"), + ("๐Ÿ‡ธ๐Ÿ‡ช Sweden", "SWE"), + ("๐Ÿ‡ธ๐Ÿ‡ฉ Sudan", "SDN"), + ("๐Ÿ‡ธ๐Ÿ‡ฆ Saudi Arabia", "SAU"), + ("๐Ÿ‡ท๐Ÿ‡บ Russian Federation", "RUS"), + ("๐Ÿ‡ท๐Ÿ‡ธ Serbia and Montenegro", "SCG"), + ("๐Ÿ‡ท๐Ÿ‡ด Romania", "ROU"), + ("๐Ÿ‡ถ๐Ÿ‡ฆ Qatar", "QAT"), + ("๐Ÿ‡ต๐Ÿ‡พ Paraguay", "PRY"), + ("๐Ÿ‡ต๐Ÿ‡น Portugal", "PRT"), + ("๐Ÿ‡ต๐Ÿ‡ฑ Poland", "POL"), + ("๐Ÿ‡ต๐Ÿ‡ฐ Pakistan", "PAK"), + ("๐Ÿ‡ต๐Ÿ‡ญ Philippines", "PHL"), + ("๐Ÿ‡ต๐Ÿ‡ช Peru", "PER"), + ("๐Ÿ‡ต๐Ÿ‡ฆ Panama", "PAN"), + ("๐Ÿ‡ด๐Ÿ‡ฒ Oman", "OMN"), + ("๐Ÿ‡ณ๐Ÿ‡ฟ New Zealand", "NZL"), + ("๐Ÿ‡ณ๐Ÿ‡ต Nepal", "NPL"), + ("๐Ÿ‡ณ๐Ÿ‡ด Norway", "NOR"), + ("๐Ÿ‡ณ๐Ÿ‡ฑ Netherlands", "NLD"), + ("๐Ÿ‡ณ๐Ÿ‡ฎ Nicaragua", "NIC"), + ("๐Ÿ‡ณ๐Ÿ‡ฌ Nigeria", "NGA"), + ("๐Ÿ‡ณ๐Ÿ‡ฆ Namibia", "NAM"), + ("๐Ÿ‡ฒ๐Ÿ‡ฟ Mozambique", "MOZ"), + ("๐Ÿ‡ฒ๐Ÿ‡พ Malaysia", "MYS"), + ("๐Ÿ‡ฒ๐Ÿ‡ฝ Mexico", "MEX"), + ("๐Ÿ‡ฒ๐Ÿ‡น Malta", "MLT"), + ("๐Ÿ‡ฒ๐Ÿ‡ณ Mongolia", "MNG"), + ("๐Ÿ‡ฒ๐Ÿ‡ฒ Myanmar", "MMR"), + ("๐Ÿ‡ฒ๐Ÿ‡ฐ North Macedonia", "MKD"), + ("๐Ÿ‡ฒ๐Ÿ‡ฉ Moldova", "MDA"), + ("๐Ÿ‡ฒ๐Ÿ‡ฆ Morocco", "MAR"), + ("๐Ÿ‡ฑ๐Ÿ‡พ Libya", "LBY"), + ("๐Ÿ‡ฑ๐Ÿ‡ป Latvia", "LVA"), + ("๐Ÿ‡ฑ๐Ÿ‡บ Luxembourg", "LUX"), + ("๐Ÿ‡ฑ๐Ÿ‡น Lithuania", "LTU"), + ("๐Ÿ‡ฑ๐Ÿ‡ฐ Sri Lanka", "LKA"), + ("๐Ÿ‡ฑ๐Ÿ‡ง Lebanon", "LBN"), + ("๐Ÿ‡ฐ๐Ÿ‡ฟ Kazakhstan", "KAZ"), + ("๐Ÿ‡ฐ๐Ÿ‡ผ Kuwait", "KWT"), + ("๐Ÿ‡ฐ๐Ÿ‡ท South Korea", "KOR"), + ("๐Ÿ‡ฐ๐Ÿ‡ต North Korea", "PRK"), + ("๐Ÿ‡ฐ๐Ÿ‡ญ Cambodia", "KHM"), + ("๐Ÿ‡ฐ๐Ÿ‡ฌ Kyrgyz Republic", "KGZ"), + ("๐Ÿ‡ฐ๐Ÿ‡ช Kenya", "KEN"), + ("๐Ÿ‡ฏ๐Ÿ‡ต Japan", "JPN"), + ("๐Ÿ‡ฏ๐Ÿ‡ด Jordan", "JOR"), + ("๐Ÿ‡ฏ๐Ÿ‡ฒ Jamaica", "JAM"), + ("๐Ÿ‡ฎ๐Ÿ‡น Italy", "ITA"), + ("๐Ÿ‡ฎ๐Ÿ‡ธ Iceland", "ISL"), + ("๐Ÿ‡ฎ๐Ÿ‡ท Iran", "IRN"), + ("๐Ÿ‡ฎ๐Ÿ‡ถ Iraq", "IRQ"), + ("๐Ÿ‡ฎ๐Ÿ‡ณ India", "IND"), + ("๐Ÿ‡ฎ๐Ÿ‡ฑ Israel", "ISR"), + ("๐Ÿ‡ฎ๐Ÿ‡ช Ireland", "IRL"), + ("๐Ÿ‡ฎ๐Ÿ‡ฉ Indonesia", "IDN"), + ("๐Ÿ‡ญ๐Ÿ‡บ Hungary", "HUN"), + ("๐Ÿ‡ญ๐Ÿ‡น Haiti", "HTI"), + ("๐Ÿ‡ญ๐Ÿ‡ท Croatia", "HRV"), + ("๐Ÿ‡ญ๐Ÿ‡ณ Honduras", "HND"), + ("๐Ÿ‡ญ๐Ÿ‡ฐ Hong Kong", "HKG"), + ("๐Ÿ‡ฌ๐Ÿ‡น Guatemala", "GTM"), + ("๐Ÿ‡ฌ๐Ÿ‡ท Greece", "GRC"), + ("๐Ÿ‡ฌ๐Ÿ‡ฎ Gibraltar", "GIB"), + ("๐Ÿ‡ฌ๐Ÿ‡ญ Ghana", "GHA"), + ("๐Ÿ‡ฌ๐Ÿ‡ช Georgia", "GEO"), + ("๐Ÿ‡ฌ๐Ÿ‡ง United Kingdom", "GBR"), + ("๐Ÿ‡ฌ๐Ÿ‡ฆ Gabon", "GAB"), + ("๐Ÿ‡ซ๐Ÿ‡ท France", "FRA"), + ("๐Ÿ‡ซ๐Ÿ‡ฎ Finland", "FIN"), + ("๐Ÿ‡ช๐Ÿ‡น Ethiopia", "ETH"), + ("๐Ÿ‡ช๐Ÿ‡ธ Spain", "ESP"), + ("๐Ÿ‡ช๐Ÿ‡ท Eritrea", "ERI"), + ("๐Ÿ‡ช๐Ÿ‡ฌ Egypt", "EGY"), + ("๐Ÿ‡ช๐Ÿ‡ช Estonia", "EST"), + ("๐Ÿ‡ช๐Ÿ‡จ Ecuador", "ECU"), + ("๐Ÿ‡ฉ๐Ÿ‡ฟ Algeria", "DZA"), + ("๐Ÿ‡ฉ๐Ÿ‡ด Dominican Republic", "DOM"), + ("๐Ÿ‡ฉ๐Ÿ‡ฐ Denmark", "DNK"), + ("๐Ÿ‡ฉ๐Ÿ‡ช Germany", "DEU"), + ("๐Ÿ‡จ๐Ÿ‡ฟ Czech Republic", "CZE"), + ("๐Ÿ‡จ๐Ÿ‡พ Cyprus", "CYP"), + ("๐Ÿ‡จ๐Ÿ‡บ Cuba", "CUB"), + ("๐Ÿ‡จ๐Ÿ‡ท Costa Rica", "CRI"), + ("๐Ÿ‡จ๐Ÿ‡ด Colombia", "COL"), + ("๐Ÿ‡จ๐Ÿ‡ณ China", "CHN"), + ("๐Ÿ‡จ๐Ÿ‡ฒ Cameroon", "CMR"), + ("๐Ÿ‡จ๐Ÿ‡ฑ Chile", "CHL"), + ("๐Ÿ‡จ๐Ÿ‡ฎ Cote d'Ivoire", "CIV"), + ("๐Ÿ‡จ๐Ÿ‡ญ Switzerland", "CHE"), + ("๐Ÿ‡จ๐Ÿ‡ฌ Congo", "COG"), + ("๐Ÿ‡จ๐Ÿ‡ฉ Democratic Republic of the Congo", "COD"), + ("๐Ÿ‡จ๐Ÿ‡ฆ Canada", "CAN"), + ("๐Ÿ‡ง๐Ÿ‡พ Belarus", "BLR"), + ("๐Ÿ‡ง๐Ÿ‡ผ Botswana", "BWA"), + ("๐Ÿ‡ง๐Ÿ‡ท Brazil", "BRA"), + ("๐Ÿ‡ง๐Ÿ‡ด Bolivia", "BOL"), + ("๐Ÿ‡ง๐Ÿ‡ณ Brunei", "BRN"), + ("๐Ÿ‡ง๐Ÿ‡ฏ Benin", "BEN"), + ("๐Ÿ‡ง๐Ÿ‡ญ Bahrain", "BHR"), + ("๐Ÿ‡ง๐Ÿ‡ฌ Bulgaria", "BGR"), + ("๐Ÿ‡ง๐Ÿ‡ช Belgium", "BEL"), + ("๐Ÿ‡ง๐Ÿ‡ฉ Bangladesh", "BGD"), + ("๐Ÿ‡ง๐Ÿ‡ฆ Bosnia and Herzegovina", "BIH"), + ("๐Ÿ‡ฆ๐Ÿ‡ฟ Azerbaijan", "AZE"), + ("๐Ÿ‡ฆ๐Ÿ‡บ Australia", "AUS"), + ("๐Ÿ‡ฆ๐Ÿ‡น Austria", "AUT"), + ("๐Ÿ‡ฆ๐Ÿ‡ท Argentina", "ARG"), + ("๐Ÿ‡ฆ๐Ÿ‡ด Angola", "AGO"), + ("๐Ÿ‡ฆ Netherlands Antilles", "ANT"), + ("๐Ÿ‡ฆ๐Ÿ‡ฒ Armenia", "ARM"), + ("๐Ÿ‡ฆ๐Ÿ‡ฑ Albania", "ALB"), + ("๐Ÿ‡ฆ๐Ÿ‡ช United Arab Emirates", "ARE") +] + + +def find_electricity_mix(code: str): + # TODO: Maybe more optimal to construct database at the beginning of the app + # in the same fashion as find_model + res = [] + with open(PATH) as fd: + csv = DictReader(fd) + for row in csv: + res += [float(row[code])] + return res + +def dataframe_electricity_mix(countries: list): + + df = pd.read_csv('src/data/electricity_mix.csv') + df['name_unit'] = df['name'] + ' (' + df['unit'] + ')' + df = df[['name_unit'] + [x[1] for x in COUNTRY_CODES if x[0] in countries]] + + df_melted = df.melt( + id_vars=['name_unit'], + value_vars=[x[1] for x in COUNTRY_CODES if x[0] in countries], + var_name='country', + value_name='value') + + df = df_melted.pivot(columns='name_unit', + index='country', + values='value') + + return df \ No newline at end of file diff --git a/expert.py b/expert.py new file mode 100755 index 0000000000000000000000000000000000000000..700fb629a20143b0d6a8830c01a82a85f5ece698 --- /dev/null +++ b/expert.py @@ -0,0 +1,156 @@ +import streamlit as st +import pandas as pd +from ecologits.impacts.llm import compute_llm_impacts + +from src.utils import format_impacts, average_range_impacts, format_impacts_expert, model_active_params_fn, model_total_params_fn +from src.impacts import display_impacts +#from src.constants import PROVIDERS, MODELS +from src.electricity_mix import COUNTRY_CODES, find_electricity_mix, dataframe_electricity_mix +from ecologits.model_repository import models + +import plotly.express as px + +def reset_model(): + model = 'CUSTOM' + +def expert_mode(): + + st.markdown("### ๐Ÿค“ Expert mode") + + with st.container(border = True): + + ########## Model info ########## + + # col1, col2, col3 = st.columns(3) + + # with col1: + # provider = st.selectbox(label = 'Provider expert', + # options = [x[0] for x in PROVIDERS], + # index = 0) + # provider = [x[1] for x in PROVIDERS if x[0] == provider][0] + # if 'huggingface_hub' in provider: + # provider = 'huggingface_hub' + + # with col2: + # model = st.selectbox('Model expert', [x[0] for x in MODELS if provider in x[1]]) + # model = [x[1] for x in MODELS if x[0] == model][0].split('/', 1)[1] + + ########## Model parameters ########## + + col11, col22, col33 = st.columns(3) + + with col11: + # st.write(provider, model) + # st.write(models.find_model(provider, model)) + # st.write(model_active_params_fn(provider, model, 45)) + active_params = st.number_input('Active parameters (B)', 0, None, 45) + + with col22: + total_params = st.number_input('Total parameters (B)', 0, None, 45) + + with col33: + output_tokens = st.number_input('Output completion tokens', 100) + + ########## Electricity mix ########## + + location = st.selectbox('Location', [x[0] for x in COUNTRY_CODES]) + + col4, col5, col6 = st.columns(3) + + with col4: + mix_gwp = st.number_input('Electricity mix - GHG emissions [kgCO2eq / kWh]', find_electricity_mix([x[1] for x in COUNTRY_CODES if x[0] ==location][0])[2], format="%0.6f") + #disp_ranges = st.toggle('Display impact ranges', False) + with col5: + mix_adpe = st.number_input('Electricity mix - Abiotic resources [kgSbeq / kWh]', find_electricity_mix([x[1] for x in COUNTRY_CODES if x[0] ==location][0])[0], format="%0.13f") + with col6: + mix_pe = st.number_input('Electricity mix - Primary energy [MJ / kWh]', find_electricity_mix([x[1] for x in COUNTRY_CODES if x[0] ==location][0])[1], format="%0.3f") + + impacts = compute_llm_impacts(model_active_parameter_count=active_params, + model_total_parameter_count=total_params, + output_token_count=output_tokens, + request_latency=100000, + if_electricity_mix_gwp=mix_gwp, + if_electricity_mix_adpe=mix_adpe, + if_electricity_mix_pe=mix_pe + ) + + impacts, usage, embodied = format_impacts(impacts) + + with st.container(border = True): + + st.markdown('

Environmental Impacts

', unsafe_allow_html = True) + + display_impacts(impacts) + + with st.expander('โš–๏ธ Usage vs Embodied'): + + st.markdown('

Embodied vs Usage comparison

', unsafe_allow_html = True) + + st.markdown('The usage impacts account for the electricity consumption of the model while the embodied impacts account for resource extraction (e.g., minerals and metals), manufacturing, and transportation of the hardware.') + + col_ghg_comparison, col_adpe_comparison, col_pe_comparison = st.columns(3) + + with col_ghg_comparison: + fig_gwp = px.pie( + values = [average_range_impacts(usage.gwp.value), average_range_impacts(embodied.gwp.value)], + names = ['usage', 'embodied'], + title = 'GHG emissions', + color_discrete_sequence=["#636EFA", "#00CC96"], + width = 100 + ) + fig_gwp.update_layout(showlegend=False, title_x=0.5) + + st.plotly_chart(fig_gwp) + + with col_adpe_comparison: + fig_adpe = px.pie( + values = [average_range_impacts(usage.adpe.value), average_range_impacts(embodied.adpe.value)], + names = ['usage', 'embodied'], + title = 'Abiotic depletion', + color_discrete_sequence=["#00CC96","#636EFA"], + width = 100) + fig_adpe.update_layout( + showlegend=True, + legend=dict(yanchor="bottom", x = 0.35, y = -0.1), + title_x=0.5) + + st.plotly_chart(fig_adpe) + + with col_pe_comparison: + fig_pe = px.pie( + values = [average_range_impacts(usage.pe.value), average_range_impacts(embodied.pe.value)], + names = ['usage', 'embodied'], + title = 'Primary energy', + color_discrete_sequence=["#636EFA", "#00CC96"], + width = 100) + fig_pe.update_layout(showlegend=False, title_x=0.5) + + st.plotly_chart(fig_pe) + + with st.expander('๐ŸŒ๏ธ Location impact'): + + st.markdown('

How can location impact the footprint ?

', unsafe_allow_html = True) + + countries_to_compare = st.multiselect( + label = 'Countries to compare', + options = [x[0] for x in COUNTRY_CODES], + default = ["๐Ÿ‡ซ๐Ÿ‡ท France", "๐Ÿ‡บ๐Ÿ‡ธ United States", "๐Ÿ‡จ๐Ÿ‡ณ China"] + ) + + try: + + df = dataframe_electricity_mix(countries_to_compare) + + impact_type = st.selectbox( + label='Select an impact type to compare', + options=[x for x in df.columns if x!='country'], + index=1) + + df.sort_values(by = impact_type, inplace = True) + + fig_2 = px.bar(df, x = df.index, y = impact_type, text = impact_type, color = impact_type) + st.plotly_chart(fig_2) + + except: + + st.warning("Can't display chart with no values.") \ No newline at end of file diff --git a/impacts.py b/impacts.py new file mode 100755 index 0000000000000000000000000000000000000000..4040e86409f3dd3978d752e303f48691bfe0a47c --- /dev/null +++ b/impacts.py @@ -0,0 +1,116 @@ +import streamlit as st +import ecologits +from src.utils import ( + format_energy_eq_electric_vehicle, + format_energy_eq_electricity_consumption_ireland, + format_energy_eq_electricity_production, + format_energy_eq_physical_activity, + format_gwp_eq_airplane_paris_nyc, + format_gwp_eq_streaming, + PhysicalActivity, + EnergyProduction +) + +############################################################################################################ + +def get_impacts(model, active_params, total_params, mix_ghg, mix_adpe, mix_pe): + + return 1 + +############################################################################################################ + + +def display_impacts(impacts): + + st.divider() + + col_energy, col_ghg, col_adpe, col_pe, col_water = st.columns(5) + + with col_energy: + st.markdown('

โšก๏ธ Energy

', unsafe_allow_html = True) + st.latex(f'\Large {impacts.energy.magnitude:.3g} \ \large {impacts.energy.units}') + st.markdown(f'

Evaluates the electricity consumption

', unsafe_allow_html = True) + + with col_ghg: + st.markdown('

๐ŸŒ๏ธ GHG Emissions

', unsafe_allow_html = True) + st.latex(f'\Large {impacts.gwp.magnitude:.3g} \ \large {impacts.gwp.units}') + st.markdown(f'

Evaluates the effect on global warming

', unsafe_allow_html = True) + + with col_adpe: + st.markdown('

๐Ÿชจ Abiotic Resources

', unsafe_allow_html = True) + st.latex(f'\Large {impacts.adpe.magnitude:.3g} \ \large {impacts.adpe.units}') + st.markdown(f'

Evaluates the use of metals and minerals

', unsafe_allow_html = True) + + with col_pe: + st.markdown('

โ›ฝ๏ธ Primary Energy

', unsafe_allow_html = True) + st.latex(f'\Large {impacts.pe.magnitude:.3g} \ \large {impacts.pe.units}') + st.markdown(f'

Evaluates the use of energy resources

', unsafe_allow_html = True) + + with col_water: + st.markdown('

๐Ÿšฐ Water

', unsafe_allow_html = True) + st.latex('\Large Upcoming...') + st.markdown(f'

Evaluates the use of water

', unsafe_allow_html = True) + +############################################################################################################ + +def display_equivalent(impacts): + + st.divider() + + ev_eq = format_energy_eq_electric_vehicle(impacts.energy) + + streaming_eq = format_gwp_eq_streaming(impacts.gwp) + + col1, col2, col3 = st.columns(3) + + with col1: + physical_activity, distance = format_energy_eq_physical_activity(impacts.energy) + if physical_activity == PhysicalActivity.WALKING: + physical_activity = "๐Ÿšถ " + physical_activity.capitalize() + if physical_activity == PhysicalActivity.RUNNING: + physical_activity = "๐Ÿƒ " + physical_activity.capitalize() + + st.markdown(f'

{physical_activity}

', unsafe_allow_html = True) + st.latex(f'\Large {distance.magnitude:.3g} \ \large {distance.units}') + st.markdown(f'

Based on energy consumption

', unsafe_allow_html = True) + + with col2: + ev_eq = format_energy_eq_electric_vehicle(impacts.energy) + st.markdown(f'

๐Ÿ”‹ Electric Vehicle

', unsafe_allow_html = True) + st.latex(f'\Large {ev_eq.magnitude:.3g} \ \large {ev_eq.units}') + st.markdown(f'

Based on energy consumption

', unsafe_allow_html = True) + + with col3: + streaming_eq = format_gwp_eq_streaming(impacts.gwp) + st.markdown(f'

โฏ๏ธ Streaming

', unsafe_allow_html = True) + st.latex(f'\Large {streaming_eq.magnitude:.3g} \ \large {streaming_eq.units}') + st.markdown(f'

Based on GHG emissions

', unsafe_allow_html = True) + + st.divider() + + st.markdown('

What if 1% of the planet does this request everyday for 1 year ?

', unsafe_allow_html = True) + st.markdown('

If this use case is largely deployed around the world, the equivalent impacts would be the impacts of this request x 1% of 8 billion people x 365 days in a year.

', unsafe_allow_html = True) + + col4, col5, col6 = st.columns(3) + + with col4: + + electricity_production, count = format_energy_eq_electricity_production(impacts.energy) + if electricity_production == EnergyProduction.NUCLEAR: + emoji = "โ˜ข๏ธ" + name = "Nuclear power plants" + if electricity_production == EnergyProduction.WIND: + emoji = "๐Ÿ’จ๏ธ " + name = "Wind turbines" + st.markdown(f'

{emoji} {count.magnitude:.0f} {name} (yearly)

', unsafe_allow_html = True) + st.markdown(f'

Based on energy consumption

', unsafe_allow_html = True) + + with col5: + ireland_count = format_energy_eq_electricity_consumption_ireland(impacts.energy) + st.markdown(f'

๐Ÿ‡ฎ๐Ÿ‡ช {ireland_count.magnitude:.3f} x Ireland (yearly โšก๏ธ cons.)

', unsafe_allow_html = True) + st.markdown(f'

Based on energy consumption

', unsafe_allow_html = True) + + with col6: + paris_nyc_airplane = format_gwp_eq_airplane_paris_nyc(impacts.gwp) + st.markdown(f'

โœˆ๏ธ {round(paris_nyc_airplane.magnitude):,} Paris โ†” NYC

', unsafe_allow_html = True) + st.markdown(f'

Based on GHG emissions

', unsafe_allow_html = True) \ No newline at end of file diff --git a/models.py b/models.py new file mode 100755 index 0000000000000000000000000000000000000000..956903fd3d0df69f6d8e4ec9895f25cd153a2a01 --- /dev/null +++ b/models.py @@ -0,0 +1,46 @@ +import requests +import json +import pandas as pd +from src.constants import MODEL_REPOSITORY_URL + +def clean_models_data(df): + + dict_providers = { + 'google': 'Google', + 'mistralai': 'MistralAI', + 'meta-llama': 'Meta', + 'openai': 'OpenAI', + 'anthropic': 'Anthropic', + 'cohere': 'Cohere', + 'microsoft': 'Microsoft', + 'mistral-community': 'Mistral Community', + 'databricks': 'Databricks' + } + + df.drop('type', axis=1, inplace=True) + + df.loc[df['name'].str.contains('/'), 'name_clean'] = df.loc[df['name'].str.contains('/'), 'name'].str.split('/').str[1] + df['name_clean'] = df['name_clean'].fillna(df['name']) + df['name_clean'] = df['name_clean'].replace({'-': ' '}, regex = True) + + df.loc[df['provider'] == 'huggingface_hub', 'provider_clean'] = df.loc[df['provider'] == 'huggingface_hub', 'name'].str.split('/').str[0] + df['provider_clean'] = df['provider_clean'].fillna(df['provider']) + df['provider_clean'] = df['provider_clean'].replace(dict_providers, regex = True) + + df['architecture_type'] = df['architecture'].apply(lambda x: x['type']) + df['architecture_parameters'] = df['architecture'].apply(lambda x: x['parameters']) + + df['warnings'] = df['warnings'].apply(lambda x: ', '.join(x) if x else None).fillna('none') + df['warning_arch'] = df['warnings'].apply(lambda x: 'model-arch-not-released' in x) + df['warning_multi_modal'] = df['warnings'].apply(lambda x: 'model-arch-multimodal' in x) + + return df[['provider', 'provider_clean', 'name', 'name_clean', 'architecture_type', 'architecture_parameters', 'warning_arch', 'warning_multi_modal']] + +def load_models(): + + resp = requests.get(MODEL_REPOSITORY_URL) + data = json.loads(resp.text) + df = pd.DataFrame(data['models']) + + return clean_models_data(df) + diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000000000000000000000000000000000..352b479a916e22258d23fa2667aaea313794b1b1 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,12 @@ +[project] +name = "ecologits-calculator-streamlit" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.12" +dependencies = [ + "ecologits>=0.6.1", + "pint>=0.24.4", + "plotly>=6.0.0", + "streamlit>=1.43.1", +] diff --git a/requirements-dev.txt b/requirements-dev.txt deleted file mode 100644 index 34468efab8d8185af5c6e8642da82d98cbbed799..0000000000000000000000000000000000000000 --- a/requirements-dev.txt +++ /dev/null @@ -1,6 +0,0 @@ -gradio -ecologits==0.5.1 -pint -beautifulsoup4 -requests -tiktoken \ No newline at end of file diff --git a/requirements.txt b/requirements.txt old mode 100644 new mode 100755 index 9b17f99731be34eef108bf1b0eab003ce56be7b7..1af5d473909d1454aed5d2894ec06ff5ecc9b13d --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,4 @@ -ecologits==0.5.1 +streamlit +ecologits pint -beautifulsoup4 -requests -tiktoken \ No newline at end of file +plotly \ No newline at end of file diff --git a/src/__init__.py b/src/__init__.py new file mode 100755 index 0000000000000000000000000000000000000000..e53d4650102036d98cd320bda9d8e98feba875c8 --- /dev/null +++ b/src/__init__.py @@ -0,0 +1,8 @@ +from .content import * +from .constants import * +from .expert import expert_mode +from .utils import * +from .calculator import calculator_mode +from .impacts import get_impacts, display_impacts +from .models import load_models +from .electricity_mix import * \ No newline at end of file diff --git a/src/__pycache__/__init__.cpython-312.pyc b/src/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f855e5e9daf0fca729cbd4ea9e55d3cb8a6cc6c1 Binary files /dev/null and b/src/__pycache__/__init__.cpython-312.pyc differ diff --git a/src/__pycache__/calculator.cpython-312.pyc b/src/__pycache__/calculator.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..26b4cb670a13d3e2bb58f33a3055025045df682b Binary files /dev/null and b/src/__pycache__/calculator.cpython-312.pyc differ diff --git a/src/__pycache__/constants.cpython-312.pyc b/src/__pycache__/constants.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6e31cba5eb8d5683f32f7c1f09d664e80722b516 Binary files /dev/null and b/src/__pycache__/constants.cpython-312.pyc differ diff --git a/src/__pycache__/content.cpython-312.pyc b/src/__pycache__/content.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..52787e694c0ece26f109831c384332a76c35c851 Binary files /dev/null and b/src/__pycache__/content.cpython-312.pyc differ diff --git a/src/__pycache__/electricity_mix.cpython-312.pyc b/src/__pycache__/electricity_mix.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b10d6575f6d23ee726d51c66d5fdf19f7dfed3de Binary files /dev/null and b/src/__pycache__/electricity_mix.cpython-312.pyc differ diff --git a/src/__pycache__/expert.cpython-312.pyc b/src/__pycache__/expert.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..daecf481cb2441ac100f158599d3658cc7d899df Binary files /dev/null and b/src/__pycache__/expert.cpython-312.pyc differ diff --git a/src/__pycache__/impacts.cpython-312.pyc b/src/__pycache__/impacts.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b54fb6054e150a76d22366637970b6333db89055 Binary files /dev/null and b/src/__pycache__/impacts.cpython-312.pyc differ diff --git a/src/__pycache__/models.cpython-312.pyc b/src/__pycache__/models.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..23985face455bd8570a3150c04b3801c04f5897c Binary files /dev/null and b/src/__pycache__/models.cpython-312.pyc differ diff --git a/src/__pycache__/utils.cpython-312.pyc b/src/__pycache__/utils.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..285352d7406b8d71b43d2b0ee633837385eef5ca Binary files /dev/null and b/src/__pycache__/utils.cpython-312.pyc differ diff --git a/src/assets.py b/src/assets.py deleted file mode 100644 index bf148570b111a9e5dddb75a84ef7a5de03111d2a..0000000000000000000000000000000000000000 --- a/src/assets.py +++ /dev/null @@ -1,17 +0,0 @@ -custom_css = """ - -.descriptive-text span { - font-size: 16px !important; -} - - -.warning-box p { - background-color: #ff91001a; - border: 1px solid #f19637; - border-radius: 8px; - padding: 4px 8px; - margin: 4px 0px; - font-size: 14px !important; - font-weight: bold !important; -} -""" \ No newline at end of file diff --git a/src/calculator.py b/src/calculator.py new file mode 100755 index 0000000000000000000000000000000000000000..ea3a8c29c27b1f6361cb6ac9ad292b540d8355c1 --- /dev/null +++ b/src/calculator.py @@ -0,0 +1,67 @@ +import streamlit as st +import pandas as pd + +from ecologits.tracers.utils import llm_impacts +from src.impacts import get_impacts, display_impacts, display_equivalent +from src.utils import format_impacts +from src.content import WARNING_CLOSED_SOURCE, WARNING_MULTI_MODAL, WARNING_BOTH +from src.models import load_models, clean_models_data + +from src.constants import PROMPTS + +def calculator_mode(): + + with st.container(border=True): + + df = load_models() + + col1, col2, col3 = st.columns(3) + + with col1: + provider = st.selectbox(label = 'Provider', + options = [x for x in df['provider_clean'].unique()], + index = 9) + provider_raw = df[df['provider_clean'] == provider]['provider'].values[0] + + with col2: + model = st.selectbox('Model', [x for x in df['name_clean'].unique() if x in df[df['provider_clean'] == provider]['name_clean'].unique()]) + model_raw = df[(df['provider_clean'] == provider) & (df['name_clean'] == model)]['name'].values[0] + + with col3: + output_tokens = st.selectbox('Example prompt', [x[0] for x in PROMPTS]) + + # WARNING DISPLAY + + df_filtered = df[(df['provider_clean'] == provider) & (df['name_clean'] == model)] + + if df_filtered['warning_arch'].values[0] and not df_filtered['warning_multi_modal'].values[0]: + st.warning(WARNING_CLOSED_SOURCE) + if df_filtered['warning_multi_modal'].values[0] and not df_filtered['warning_arch'].values[0]: + st.warning(WARNING_MULTI_MODAL) + if df_filtered['warning_arch'].values[0] and df_filtered['warning_multi_modal'].values[0]: + st.warning(WARNING_BOTH) + + try: + impacts = llm_impacts( + provider=provider_raw, + model_name=model_raw, + output_token_count=[x[1] for x in PROMPTS if x[0] == output_tokens][0], + request_latency=100000 + ) + + impacts, _, _ = format_impacts(impacts) + + with st.container(border=True): + + st.markdown('

Environmental impacts

', unsafe_allow_html=True) + st.markdown('

To understand how the environmental impacts are computed go to the ๐Ÿ“– Methodology tab.

', unsafe_allow_html=True) + display_impacts(impacts) + + with st.container(border=True): + + st.markdown('

That\'s equivalent to ...

', unsafe_allow_html=True) + st.markdown('

Making this request to the LLM is equivalent to the following actions :

', unsafe_allow_html=True) + display_equivalent(impacts) + + except Exception as e: + st.error('Could not find the model in the repository. Please try another model.') \ No newline at end of file diff --git a/src/constants.py b/src/constants.py old mode 100644 new mode 100755 index 7d3a45e468664a7dd05aba1b00ce1a8c56d08bc5..01f5f17177c0e02cb2539be85e25ad6f1408e0a5 --- a/src/constants.py +++ b/src/constants.py @@ -1,103 +1,106 @@ -PROVIDERS = [ - ("OpenAI", "openai"), - ("Anthropic", "anthropic"), - ("Cohere", "cohere"), - ("Meta", "huggingface_hub/meta"), - ("Mistral AI", "mistralai"), -] - -OPENAI_MODELS = [ - ("GPT-4o", "gpt-4o"), - ("GPT-4-Turbo", "gpt-4-turbo"), - ("GPT-4", "gpt-4"), - ("GPT-3.5-Turbo", "gpt-3.5-turbo"), -] - -ANTHROPIC_MODELS = [ - ("Claude 3 Opus", "claude-3-opus-20240229"), - ("Claude 3 Sonnet", "claude-3-sonnet-20240229"), - ("Claude 3 Haiku", "claude-3-haiku-20240307"), - ("Claude 2.1", "claude-2.1"), - ("Claude 2.0", "claude-2.0"), - ("Claude Instant 1.2", "claude-instant-1.2"), -] - -COHERE_MODELS = [ - ("Command Light", "command-light"), - ("Command", "command"), - ("Command R", "command-r"), - ("Command R+", "command-r-plus"), -] - -META_MODELS = [ - ("Llama 3 8B", "meta-llama/Meta-Llama-3-8B"), - ("Llama 3 70B", "meta-llama/Meta-Llama-3-70B"), - ("Llama 2 7B", "meta-llama/Llama-2-7b-hf"), - ("Llama 2 13B", "meta-llama/Llama-2-13b-hf"), - ("Llama 2 70B", "meta-llama/Llama-2-70b-hf"), -] - -MISTRALAI_MODELS = [ - ("Mistral 7B", "open-mistral-7b"), - ("Mixtral 8x7B", "open-mixtral-8x7b"), - ("Mixtral 8x22B", "open-mixtral-8x22b"), - ("Tiny", "mistral-tiny-2312"), - ("Small", "mistral-small-2402"), - ("Medium", "mistral-medium-2312"), - ("Large", "mistral-large-2402"), -] - -PROMPTS = [ - ("Write a Tweet", 50), - ("Write an email", 170), - ("Write an article summary", 250), - ("Small conversation with a chatbot", 400), - ("Write a report of 5 pages", 5000), -] -PROMPTS = [(s + f" ({v} output tokens)", v) for (s, v) in PROMPTS] - -CLOSED_SOURCE_MODELS = { - "openai/gpt-4o", - "openai/gpt-4-turbo", - "openai/gpt-4", - "openai/gpt-3.5-turbo", - "anthropic/claude-3-opus-20240229", - "anthropic/claude-3-sonnet-20240229", - "anthropic/claude-3-haiku-20240307", - "anthropic/claude-2.1", - "anthropic/claude-2.0", - "anthropic/claude-instant-1.2", - "mistralai/mistral-tiny-2312", - "mistralai/mistral-small-2402", - "mistralai/mistral-medium-2312", - "mistralai/mistral-large-2402", -} - -MODELS = [ - ("OpenAI / GPT-4o", "openai/gpt-4o"), - ("OpenAI / GPT-4-Turbo", "openai/gpt-4-turbo"), - ("OpenAI / GPT-4", "openai/gpt-4"), - ("OpenAI / GPT-3.5-Turbo", "openai/gpt-3.5-turbo"), - ("Anthropic / Claude 3 Opus", "anthropic/claude-3-opus-20240229"), - ("Anthropic / Claude 3 Sonnet", "anthropic/claude-3-sonnet-20240229"), - ("Anthropic / Claude 3 Haiku", "anthropic/claude-3-haiku-20240307"), - ("Anthropic / Claude 2.1", "anthropic/claude-2.1"), - ("Anthropic / Claude 2.0", "anthropic/claude-2.0"), - ("Anthropic / Claude Instant 1.2", "anthropic/claude-instant-1.2"), - ("Mistral AI / Mistral 7B", "mistralai/open-mistral-7b"), - ("Mistral AI / Mixtral 8x7B", "mistralai/open-mixtral-8x7b"), - ("Mistral AI / Mixtral 8x22B", "mistralai/open-mixtral-8x22b"), - ("Mistral AI / Tiny", "mistralai/mistral-tiny-2312"), - ("Mistral AI / Small", "mistralai/mistral-small-2402"), - ("Mistral AI / Medium", "mistralai/mistral-medium-2312"), - ("Mistral AI / Large", "mistralai/mistral-large-2402"), - ("Meta / Llama 3 8B", "huggingface_hub/meta-llama/Meta-Llama-3-8B"), - ("Meta / Llama 3 70B", "huggingface_hub/meta-llama/Meta-Llama-3-70B"), - ("Meta / Llama 2 7B", "huggingface_hub/meta-llama/Llama-2-7b-hf"), - ("Meta / Llama 2 13B", "huggingface_hub/meta-llama/Llama-2-13b-hf"), - ("Meta / Llama 2 70B", "huggingface_hub/meta-llama/Llama-2-70b-hf"), - ("Cohere / Command Light", "cohere/command-light"), - ("Cohere / Command", "cohere/command"), - ("Cohere / Command R", "cohere/command-r"), - ("Cohere / Command R+", "cohere/command-r-plus"), -] +PROVIDERS = [ + ("OpenAI", "openai"), + ("Anthropic", "anthropic"), + ("Cohere", "cohere"), + ("Meta", "huggingface_hub/meta"), + ("Mistral AI", "mistralai"), +] + +OPENAI_MODELS = [ + ("GPT-4o", "gpt-4o"), + ("GPT-4-Turbo", "gpt-4-turbo"), + ("GPT-4", "gpt-4"), + ("GPT-3.5-Turbo", "gpt-3.5-turbo"), +] + +ANTHROPIC_MODELS = [ + ("Claude 3 Opus", "claude-3-opus-20240229"), + ("Claude 3 Sonnet", "claude-3-sonnet-20240229"), + ("Claude 3 Haiku", "claude-3-haiku-20240307"), + ("Claude 2.1", "claude-2.1"), + ("Claude 2.0", "claude-2.0"), + ("Claude Instant 1.2", "claude-instant-1.2"), +] + +COHERE_MODELS = [ + ("Command Light", "command-light"), + ("Command", "command"), + ("Command R", "command-r"), + ("Command R+", "command-r-plus"), +] + +META_MODELS = [ + ("Llama 3 8B", "meta-llama/Meta-Llama-3-8B"), + ("Llama 3 70B", "meta-llama/Meta-Llama-3-70B"), + ("Llama 2 7B", "meta-llama/Llama-2-7b-hf"), + ("Llama 2 13B", "meta-llama/Llama-2-13b-hf"), + ("Llama 2 70B", "meta-llama/Llama-2-70b-hf"), +] + +MISTRALAI_MODELS = [ + ("Mistral 7B", "open-mistral-7b"), + ("Mixtral 8x7B", "open-mixtral-8x7b"), + ("Mixtral 8x22B", "open-mixtral-8x22b"), + ("Tiny", "mistral-tiny-2312"), + ("Small", "mistral-small-2402"), + ("Medium", "mistral-medium-2312"), + ("Large", "mistral-large-2402"), +] + +PROMPTS = [ + ("Write a Tweet", 50), + ("Write an email", 170), + ("Write an article summary", 250), + ("Small conversation with a chatbot", 400), + ("Write a report of 5 pages", 5000), + ("Write the code for this app", 10000) +] +PROMPTS = [(s + f" ({v} output tokens)", v) for (s, v) in PROMPTS] + +CLOSED_SOURCE_MODELS = { + "openai/gpt-4o", + "openai/gpt-4-turbo", + "openai/gpt-4", + "openai/gpt-3.5-turbo", + "anthropic/claude-3-opus-20240229", + "anthropic/claude-3-sonnet-20240229", + "anthropic/claude-3-haiku-20240307", + "anthropic/claude-2.1", + "anthropic/claude-2.0", + "anthropic/claude-instant-1.2", + "mistralai/mistral-tiny-2312", + "mistralai/mistral-small-2402", + "mistralai/mistral-medium-2312", + "mistralai/mistral-large-2402", +} + +MODELS = [ + ("OpenAI / GPT-4o", "openai/gpt-4o"), + ("OpenAI / GPT-4-Turbo", "openai/gpt-4-turbo"), + ("OpenAI / GPT-4", "openai/gpt-4"), + ("OpenAI / GPT-3.5-Turbo", "openai/gpt-3.5-turbo"), + ("Anthropic / Claude 3 Opus", "anthropic/claude-3-opus-20240229"), + ("Anthropic / Claude 3 Sonnet", "anthropic/claude-3-sonnet-20240229"), + ("Anthropic / Claude 3 Haiku", "anthropic/claude-3-haiku-20240307"), + ("Anthropic / Claude 2.1", "anthropic/claude-2.1"), + ("Anthropic / Claude 2.0", "anthropic/claude-2.0"), + ("Anthropic / Claude Instant 1.2", "anthropic/claude-instant-1.2"), + ("Mistral AI / Mistral 7B", "mistralai/open-mistral-7b"), + ("Mistral AI / Mixtral 8x7B", "mistralai/open-mixtral-8x7b"), + ("Mistral AI / Mixtral 8x22B", "mistralai/open-mixtral-8x22b"), + ("Mistral AI / Tiny", "mistralai/mistral-tiny-2312"), + ("Mistral AI / Small", "mistralai/mistral-small-2402"), + ("Mistral AI / Medium", "mistralai/mistral-medium-2312"), + ("Mistral AI / Large", "mistralai/mistral-large-2402"), + ("Meta / Llama 3 8B", "huggingface_hub/meta-llama/Meta-Llama-3-8B"), + ("Meta / Llama 3 70B", "huggingface_hub/meta-llama/Meta-Llama-3-70B"), + ("Meta / Llama 2 7B", "huggingface_hub/meta-llama/Llama-2-7b-hf"), + ("Meta / Llama 2 13B", "huggingface_hub/meta-llama/Llama-2-13b-hf"), + ("Meta / Llama 2 70B", "huggingface_hub/meta-llama/Llama-2-70b-hf"), + ("Cohere / Command Light", "cohere/command-light"), + ("Cohere / Command", "cohere/command"), + ("Cohere / Command R", "cohere/command-r"), + ("Cohere / Command R+", "cohere/command-r-plus"), +] + +MODEL_REPOSITORY_URL = "https://raw.githubusercontent.com/genai-impact/ecologits/refs/heads/main/ecologits/data/models.json" \ No newline at end of file diff --git a/src/content.py b/src/content.py old mode 100644 new mode 100755 index 69e78816887f4e7bfd3905120599f2e284e97663..e1503dc72bcbe2c3cd89a41479da761ff41996d5 --- a/src/content.py +++ b/src/content.py @@ -1,286 +1,232 @@ - -HERO_TEXT = """ - - -

๐Ÿงฎ EcoLogits Calculator

-
-

- EcoLogits is a python library that tracks the energy consumption and environmental - footprint of using generative AI models through APIs. -

-
-
- -This tool is developed and maintained by [GenAI Impact](https://genai-impact.org/) non-profit. Learn more about -๐ŸŒฑ EcoLogits by reading the documentation on [ecologits.ai](https://ecologits.ai). - -๐Ÿฉท Support us by giving a โญ๏ธ on our [GitHub repository](https://github.com/genai-impact/ecologits) and by following our [LinkedIn page](https://www.linkedin.com/company/genai-impact/). -""" - -ABOUT_TEXT = r""" -## ๐ŸŽฏ Our goal - -**The main goal of the EcoLogits Calculator is to raise awareness on the environmental impacts of LLM inference.** - -The rapid evolution of generative AI is reshaping numerous industries and aspects of our daily lives. While these -advancements offer some benefits, they also **pose substantial environmental challenges that cannot be overlooked**. -Plus the issue of AI's environmental footprint has been mainly discussed at training stage but rarely at the inference -stage. That is an issue because **inference impacts for LLMs can largely overcome the training impacts when deployed -at large scales**. - -At **[GenAI Impact](https://genai-impact.org/) we are dedicated to understanding and mitigating the environmental -impacts of generative AI** through rigorous research, innovative tools, and community engagement. Especially, in early -2024 we have launched an new open-source tool called [EcoLogits](https://github.com/genai-impact/ecologits) that tracks -the energy consumption and environmental footprint of using generative AI models through APIs. - -## ๐Ÿ™‹ FAQ - -**How we assess the impacts of closed-source models?** - -Environmental impacts are calculated based on model architecture and parameter count. For closed-source models, we -lack transparency from providers, so we estimate parameter counts using available information. For GPT models, we -based our estimates on leaked GPT-4 architecture and scaled parameters count for GPT-4-Turbo and GPT-4o based on -pricing differences. For other proprietary models like Anthropic's Claude, we assume similar impacts for models -released around the same time with similar performance on public benchmarks. Please note that these estimates are -based on assumptions and may not be exact. Our methods are open-source and transparent so you can always see the -hypotheses we use. - -**Which generative AI models or providers are supported?** - -To see the full list of **generative AI providers** currently supported by EcoLogits, see the following -[documentation page](https://ecologits.ai/providers/). As of today we only support LLMs but we plan to add support for -embeddings, image generation, multi-modal models and more. If you are interested don't hesitate to -[join us](https://genai-impact.org/contact/) and accelerate our work! - -**How to reduce AI environmental impacts?** - -* Look at **indirect impacts** of your project. Does the finality of your project is impacting negatively the -environment? -* **Be frugal** and question your usage or need of AI - * Do you really need AI to solve your problem? - * Do you really need GenAI to solve your problem? (you can read this [paper](https://aclanthology.org/2023.emnlp-industry.39.pdf)) - * Use small and specialized models to solve your problem. - * Evaluate before, during and after the development of your project the environmental impacts with tools like - ๐ŸŒฑ [EcoLogits](https://github.com/genai-impact/ecologits) or [CodeCarbon](https://github.com/mlco2/codecarbon) - (see [more tools](https://github.com/samuelrince/awesome-green-ai)) - * Restrict the use case and limit the usage of your tool or feature to the desired purpose. -* Do NOT buy new GPUs / hardware - * Hardware manufacturing for data centers is around 50% of the impact. -* Use cloud instances that are located in low emissions / high energy efficiency data centers -(see [electricitymaps.com](https://app.electricitymaps.com/map)) -* Optimize your models for production - * Quantize your models. - * Use inference optimization tricks. - * Prefer fine-tuning of small and existing models over generalist models. - -**What is the difference between **EcoLogits** and [CodeCarbon](https://github.com/mlco2/codecarbon)?** - -EcoLogits is focused on estimating the environmental impacts of generative AI (only LLMs for now) used **through API -providers (such as OpenAI, Anthropic, Cloud APIs...)** whereas CodeCarbon is more general tool to measure energy -consumption and estimate GHG emissions measurement. If you deploy LLMs locally we encourage you to use CodeCarbon to -get real numbers of your energy consumption. - -## ๐Ÿค— Contributing - -We are eager to get feedback from the community, don't hesitate to engage the discussion with us on this -[GitHub thread](https://github.com/genai-impact/ecologits/discussions/45) or message us on -[LinkedIn](https://www.linkedin.com/company/genai-impact/). - -We also welcome any open-source contributions on ๐ŸŒฑ **[EcoLogits](https://github.com/genai-impact/ecologits)** or on -๐Ÿงฎ **EcoLogits Calculator**. - -## โš–๏ธ License - -

- This work is licensed under - - CC BY-SA 4.0 - - - - -

- -## ๐Ÿ™Œ Acknowledgement - -We thank [Data For Good](https://dataforgood.fr/) and [Boavizta](https://boavizta.org/en) for supporting the -development of this project. Their contributions of tools, best practices, and expertise in environmental impact -assessment have been invaluable. - -We also extend our gratitude to the open-source contributions of ๐Ÿค— [Hugging Face](huggingface.com) on the LLM-Perf -Leaderboard. - -## ๐Ÿค Contact - -For general question on the project, please use the [GitHub thread](https://github.com/genai-impact/ecologits/discussions/45). -Otherwise use our contact form on [genai-impact.org/contact](https://genai-impact.org/contact/). -""" - - -METHODOLOGY_TEXT = r""" -## ๐Ÿ“– Methodology - -We have developed a methodology to **estimate the energy consumption and environmental impacts for an LLM inference** -based on request parameters and hypotheses on the data center location, the hardware used, the model architecture and -more. - -In this section we will only cover the principles of the methodology related to the ๐Ÿงฎ **EcoLogits Calculator**. If -you wish to learn more on the environmental impacts modeling of an LLM request checkout the -๐ŸŒฑ [EcoLogits documentation page](https://ecologits.ai/methodology/). - -### Modeling impacts of an LLM request - -The environmental impacts of an LLM inference are split into the **usage impacts** $I_{request}^u$ to account for -electricity consumption and the **embodied impacts** $I_{request}^e$ that relates to resource extraction, hardware -manufacturing and transportation. In general terms it can be expressed as follow: - -$$ I_{request} = I_{request}^u + I_{request}^e $$ - -$$ I_{request} = E_{request}*F_{em}+\frac{\Delta T}{\Delta L}*I_{server}^e $$ - -With, - -* $E_{request}$ the estimated energy consumption of the server and its cooling system. -* $F_{em}$ the electricity mix that depends on the country and time. -* $\frac{\Delta T}{\Delta L}$ the hardware usage ratio i.e. the computation time over the lifetime of the hardware. -* $I_{server}^e$ the embodied impacts of the server. - -Additionally, to โšก๏ธ **direct energy consumption** the environmental impacts are expressed in **three dimensions -(multi-criteria impacts)** that are: - -* ๐ŸŒ **Global Warming Potential** (GWP): Potential impact on global warming in kgCO2eq (commonly known as GHG/carbon -emissions). -* ๐Ÿชจ **Abiotic Depletion Potential for Elements** (ADPe): Impact on the depletion of non-living resources such as -minerals or metals in kgSbeq. -* โ›ฝ๏ธ **Primary Energy** (PE): Total energy consumed from primary sources in MJ. - -### Principles, Data and Hypotheses - -We use a **bottom-up methodology** to model impacts, meaning that we will estimate the impacts of low-level physical -components to then estimate the impacts at software level (in that case an LLM inference). We also rely on **Life -Cycle Approach (LCA) proxies and approach** to model both usage and embodied phases with multi-criteria impacts. -If you are interested in this approach we recommend you to read the following [Boavizta](https://boavizta.org/) -resources. - -* [Digital & environment: How to evaluate server manufacturing footprint, beyond greenhouse gas emissions?](https://boavizta.org/en/blog/empreinte-de-la-fabrication-d-un-serveur) -* [Boavizta API automated evaluation of environmental impacts of ICT services and equipments](https://boavizta.org/en/blog/boavizta-api-automated-evaluation-of-ict-impacts-on-the-environment) -* [Boavizta API documentation](https://doc.api.boavizta.org/) - -We leverage **open data to estimate the environmental impacts**, here is an exhaustive list of our data providers. - -* [LLM-Perf Leaderboard](https://huggingface.co/spaces/optimum/llm-perf-leaderboard) to estimate GPU energy consumption -and latency based on the model architecture and number of output tokens. -* [Boavizta API](https://github.com/Boavizta/boaviztapi) to estimate server embodied impacts and base energy -consumption. -* [ADEME Base Empreinteยฎ](https://base-empreinte.ademe.fr/) for electricity mix impacts per country. - -Finally here are the **main hypotheses** we have made to compute the impacts. - -* โš ๏ธ **We *"guesstimate"* the model architecture of proprietary LLMs when not disclosed by the provider.** -* Production setup: quantized models running on data center grade servers and GPUs such as A100. -* Electricity mix does not depend on time (help us enhance EcoLogits and work on this [issue](https://github.com/genai-impact/ecologits/issues/42)) -* Ignore the following impacts: unused cloud resources, data center building, network and end-user devices... (for now) - -## Equivalents - -We have integrated impact equivalents to help people better understand the impacts and have reference points for -standard use cases and everyday activities. - -### Request impacts - -These equivalents are computed based on the request impacts only. - -#### ๐Ÿšถโ€โ™‚๏ธโ€โžก๏ธ Walking or ๐Ÿƒโ€โ™‚๏ธโ€โžก๏ธ running distance - -We compare the โšก๏ธ direct energy consumption with the energy consumption of someone ๐Ÿšถโ€โ™‚๏ธโ€โžก๏ธ walking or ๐Ÿƒโ€โ™‚๏ธโ€โžก๏ธ running. -From [runningtools.com](https://www.runningtools.com/energyusage.htm) we consider the following energy values per -physical activity (for someone weighing 70kg): - -* ๐Ÿšถโ€โ™‚๏ธโ€โžก๏ธ walking: $ 196\ kJ/km $ (speed of $ 3\ km/h $) -* ๐Ÿƒโ€โ™‚๏ธโ€โžก๏ธ running: $ 294\ kJ/km $ (speed of $ 10\ km/h $) - -We divide the request energy consumption by these values to compute the distance traveled. - -#### ๐Ÿ”‹ Electric Vehicle distance - -We compare the โšก๏ธ direct energy consumption with the energy consumer by a EV car. From -[selectra.info](https://selectra.info/energie/actualites/insolite/consommation-vehicules-electriques-france-2040) or -[tesla.com](https://www.tesla.com/fr_fr/support/power-consumption) we consider an average value of energy consumed per -kilometer of: $ 0.17\ kWh/km $. - -We divide the request energy consumption by this value to compute the distance driven by an EV. - -#### โฏ๏ธ Streaming time - -We compare the ๐ŸŒ GHG emissions of the request and of streaming a video. From -[impactco2.fr](https://impactco2.fr/outils/comparateur?value=1&comparisons=streamingvideo), we consider that -$ 1\ kgCO2eq $ is equivalent to $ 15.6\ h $ of streaming. - -We multiply that value by the GHG emissions of the request to get an equivalent in hours of video streaming. - -### Scaled impacts - -These equivalents are computed based on the request impacts scaled to a worldwide adoption use case. We imply that the -same request is done 1% of the planet everyday for 1 year, and then compute impact equivalents. - -$$ -I_{scaled} = I_{request} * [1 \\% \ \text{of}\ 8B\ \text{people on earth}] * 365\ \text{days} -$$ - -#### Number of ๐Ÿ’จ wind turbines or โ˜ข๏ธ nuclear plants - -We compare the โšก๏ธ direct energy consumption (scaled) by the energy production of wind turbines and nuclear power -plants. From [ecologie.gouv.fr](https://www.ecologie.gouv.fr/eolien-terrestre) we consider that a $ 2\ MW $ wind -turbine produces $ 4.2\ GWh $ a year. And from [edf.fr](https://www.edf.fr/groupe-edf/espaces-dedies/jeunes-enseignants/pour-les-jeunes/lenergie-de-a-a-z/produire-de-lelectricite/le-nucleaire-en-chiffres) -we learn that a $ 900\ MW $ nuclear power plant produces $ 6\ TWh $ a year. - -We divide the scaled energy consumption by these values to get the number of wind turbines or nuclear power plants -needed. - -#### Multiplier of ๐Ÿ‡ฎ๐Ÿ‡ช Ireland electricity consumption - -We compare the โšก๏ธ direct energy consumption (scaled) by the electricity consumption of Ireland per year. From -[wikipedia.org](https://en.wikipedia.org/wiki/List_of_countries_by_electricity_consumption) we consider the Ireland -electricity consumption to be $ 33\ TWh $ a year for a population of 5M. - -We divide the scaled energy consumption by this value to get the equivalent number of "Ireland countries". - -#### Number of โœˆ๏ธ Paris โ†” New York City flights - -We compare the ๐ŸŒ GHG emissions (scaled) of the request and of a return flight Paris โ†” New York City. From -[impactco2.fr](https://impactco2.fr/outils/comparateur?value=1&comparisons=&equivalent=avion-pny) we consider that a -return flight Paris โ†’ New York City โ†’ Paris for one passenger emits $ 1,770\ kgCO2eq $ and we consider an overall -average load of 100 passengers per flight. - -We divide the scaled GHG emissions by this value to get the equivalent number of return flights. - -**If you are motivated to help us test and enhance this methodology -[contact us](https://genai-impact.org/contact/)!** ๐Ÿ’ช -""" - -CITATION_LABEL = "BibTeX citation for EcoLogits Calculator and the EcoLogits library:" -CITATION_TEXT = r"""@misc{ecologits-calculator, - author={Samuel Rincรฉ, Adrien Banse and Valentin Defour}, - title={EcoLogits Calculator}, - year={2024}, - howpublished= {\url{https://huggingface.co/spaces/genai-impact/ecologits-calculator}}, -} -@software{ecologits, - author = {Samuel Rincรฉ, Adrien Banse, Vinh Nguyen and Luc Berton}, - publisher = {GenAI Impact}, - title = {EcoLogits: track the energy consumption and environmental footprint of using generative AI models through APIs.}, -}""" - -LICENCE_TEXT = """

- This work is licensed under - - CC BY-SA 4.0 - - - - -

""" +HERO_TEXT = """ + +

๐Ÿงฎ EcoLogits Calculator

+
+

+ EcoLogits is a python library that tracks the energy consumption and environmental + footprint of using generative AI models through APIs. +

+
+
+""" + +INTRO_TEXT = """ +This tool is developed and maintained by [GenAI Impact](https://genai-impact.org/) non-profit. Learn more about +๐ŸŒฑ EcoLogits by reading the documentation on [ecologits.ai](https://ecologits.ai). + +๐Ÿฉท Support us by giving a โญ๏ธ on our [GitHub repository](https://github.com/genai-impact/ecologits) and by following our [LinkedIn page](https://www.linkedin.com/company/genai-impact/). +""" + +WARNING_CLOSED_SOURCE = """ +โš ๏ธ The model architecture has not been publicly released, expect lower precision of estimations. +""" + +WARNING_MULTI_MODAL = """ +โš ๏ธ The model architecture is multimodal, expect lower precision of estimations. +""" + +WARNING_BOTH = """ +โš ๏ธ The model architecture has not been publicly released and is multimodal, expect lower precision of estimations. +""" + +ABOUT_TEXT = r""" +## ๐ŸŽฏ Our goal +**The main goal of the EcoLogits Calculator is to raise awareness on the environmental impacts of LLM inference.** +The rapid evolution of generative AI is reshaping numerous industries and aspects of our daily lives. While these +advancements offer some benefits, they also **pose substantial environmental challenges that cannot be overlooked**. +Plus the issue of AI's environmental footprint has been mainly discussed at training stage but rarely at the inference +stage. That is an issue because **inference impacts for LLMs can largely overcome the training impacts when deployed +at large scales**. +At **[GenAI Impact](https://genai-impact.org/) we are dedicated to understanding and mitigating the environmental +impacts of generative AI** through rigorous research, innovative tools, and community engagement. Especially, in early +2024 we have launched an new open-source tool called [EcoLogits](https://github.com/genai-impact/ecologits) that tracks +the energy consumption and environmental footprint of using generative AI models through APIs. +## ๐Ÿ™‹ FAQ +**How we assess the impacts of closed-source models?** +Environmental impacts are calculated based on model architecture and parameter count. For closed-source models, we +lack transparency from providers, so we estimate parameter counts using available information. For GPT models, we +based our estimates on leaked GPT-4 architecture and scaled parameters count for GPT-4-Turbo and GPT-4o based on +pricing differences. For other proprietary models like Anthropic's Claude, we assume similar impacts for models +released around the same time with similar performance on public benchmarks. Please note that these estimates are +based on assumptions and may not be exact. Our methods are open-source and transparent so you can always see the +hypotheses we use. +**Which generative AI models or providers are supported?** +To see the full list of **generative AI providers** currently supported by EcoLogits, see the following +[documentation page](https://ecologits.ai/providers/). As of today we only support LLMs but we plan to add support for +embeddings, image generation, multi-modal models and more. If you are interested don't hesitate to +[join us](https://genai-impact.org/contact/) and accelerate our work! +**How to reduce AI environmental impacts?** +* Look at **indirect impacts** of your project. Does the finality of your project is impacting negatively the +environment? +* **Be frugal** and question your usage or need of AI + * Do you really need AI to solve your problem? + * Do you really need GenAI to solve your problem? (you can read this [paper](https://aclanthology.org/2023.emnlp-industry.39.pdf)) + * Use small and specialized models to solve your problem. + * Evaluate before, during and after the development of your project the environmental impacts with tools like + ๐ŸŒฑ [EcoLogits](https://github.com/genai-impact/ecologits) or [CodeCarbon](https://github.com/mlco2/codecarbon) + (see [more tools](https://github.com/samuelrince/awesome-green-ai)) + * Restrict the use case and limit the usage of your tool or feature to the desired purpose. +* Do NOT buy new GPUs / hardware + * Hardware manufacturing for data centers is around 50% of the impact. +* Use cloud instances that are located in low emissions / high energy efficiency data centers +(see [electricitymaps.com](https://app.electricitymaps.com/map)) +* Optimize your models for production + * Quantize your models. + * Use inference optimization tricks. + * Prefer fine-tuning of small and existing models over generalist models. +**What is the difference between **EcoLogits** and [CodeCarbon](https://github.com/mlco2/codecarbon)?** +EcoLogits is focused on estimating the environmental impacts of generative AI (only LLMs for now) used **through API +providers (such as OpenAI, Anthropic, Cloud APIs...)** whereas CodeCarbon is more general tool to measure energy +consumption and estimate GHG emissions measurement. If you deploy LLMs locally we encourage you to use CodeCarbon to +get real numbers of your energy consumption. +## ๐Ÿค— Contributing +We are eager to get feedback from the community, don't hesitate to engage the discussion with us on this +[GitHub thread](https://github.com/genai-impact/ecologits/discussions/45) or message us on +[LinkedIn](https://www.linkedin.com/company/genai-impact/). +We also welcome any open-source contributions on ๐ŸŒฑ **[EcoLogits](https://github.com/genai-impact/ecologits)** or on +๐Ÿงฎ **EcoLogits Calculator**. +## โš–๏ธ License +

+ This work is licensed under + + CC BY-SA 4.0 + + + + +

+## ๐Ÿ™Œ Acknowledgement +We thank [Data For Good](https://dataforgood.fr/) and [Boavizta](https://boavizta.org/en) for supporting the +development of this project. Their contributions of tools, best practices, and expertise in environmental impact +assessment have been invaluable. +We also extend our gratitude to the open-source contributions of ๐Ÿค— [Hugging Face](huggingface.com) on the LLM-Perf +Leaderboard. +## ๐Ÿค Contact +For general question on the project, please use the [GitHub thread](https://github.com/genai-impact/ecologits/discussions/45). +Otherwise use our contact form on [genai-impact.org/contact](https://genai-impact.org/contact/). +""" + + +METHODOLOGY_TEXT = r""" +## ๐Ÿ“– Methodology +We have developed a methodology to **estimate the energy consumption and environmental impacts for an LLM inference** +based on request parameters and hypotheses on the data center location, the hardware used, the model architecture and +more. +In this section we will only cover the principles of the methodology related to the ๐Ÿงฎ **EcoLogits Calculator**. If +you wish to learn more on the environmental impacts modeling of an LLM request checkout the +๐ŸŒฑ [EcoLogits documentation page](https://ecologits.ai/methodology/). +### Modeling impacts of an LLM request +The environmental impacts of an LLM inference are split into the **usage impacts** $I_{request}^u$ to account for +electricity consumption and the **embodied impacts** $I_{request}^e$ that relates to resource extraction, hardware +manufacturing and transportation. In general terms it can be expressed as follow: +$$ I_{request} = I_{request}^u + I_{request}^e $$ +$$ I_{request} = E_{request}*F_{em}+\frac{\Delta T}{\Delta L}*I_{server}^e $$ +With, +* $E_{request}$ the estimated energy consumption of the server and its cooling system. +* $F_{em}$ the electricity mix that depends on the country and time. +* $\frac{\Delta T}{\Delta L}$ the hardware usage ratio i.e. the computation time over the lifetime of the hardware. +* $I_{server}^e$ the embodied impacts of the server. +Additionally, to โšก๏ธ **direct energy consumption** the environmental impacts are expressed in **three dimensions +(multi-criteria impacts)** that are: +* ๐ŸŒ **Global Warming Potential** (GWP): Potential impact on global warming in kgCO2eq (commonly known as GHG/carbon +emissions). +* ๐Ÿชจ **Abiotic Depletion Potential for Elements** (ADPe): Impact on the depletion of non-living resources such as +minerals or metals in kgSbeq. +* โ›ฝ๏ธ **Primary Energy** (PE): Total energy consumed from primary sources in MJ. +### Principles, Data and Hypotheses +We use a **bottom-up methodology** to model impacts, meaning that we will estimate the impacts of low-level physical +components to then estimate the impacts at software level (in that case an LLM inference). We also rely on **Life +Cycle Approach (LCA) proxies and approach** to model both usage and embodied phases with multi-criteria impacts. +If you are interested in this approach we recommend you to read the following [Boavizta](https://boavizta.org/) +resources. +* [Digital & environment: How to evaluate server manufacturing footprint, beyond greenhouse gas emissions?](https://boavizta.org/en/blog/empreinte-de-la-fabrication-d-un-serveur) +* [Boavizta API automated evaluation of environmental impacts of ICT services and equipments](https://boavizta.org/en/blog/boavizta-api-automated-evaluation-of-ict-impacts-on-the-environment) +* [Boavizta API documentation](https://doc.api.boavizta.org/) +We leverage **open data to estimate the environmental impacts**, here is an exhaustive list of our data providers. +* [LLM-Perf Leaderboard](https://huggingface.co/spaces/optimum/llm-perf-leaderboard) to estimate GPU energy consumption +and latency based on the model architecture and number of output tokens. +* [Boavizta API](https://github.com/Boavizta/boaviztapi) to estimate server embodied impacts and base energy +consumption. +* [ADEME Base Empreinteยฎ](https://base-empreinte.ademe.fr/) for electricity mix impacts per country. +Finally here are the **main hypotheses** we have made to compute the impacts. +* โš ๏ธ **We *"guesstimate"* the model architecture of proprietary LLMs when not disclosed by the provider.** +* Production setup: quantized models running on data center grade servers and GPUs such as A100. +* Electricity mix does not depend on time (help us enhance EcoLogits and work on this [issue](https://github.com/genai-impact/ecologits/issues/42)) +* Ignore the following impacts: unused cloud resources, data center building, network and end-user devices... (for now) +## Equivalents +We have integrated impact equivalents to help people better understand the impacts and have reference points for +standard use cases and everyday activities. +### Request impacts +These equivalents are computed based on the request impacts only. +#### ๐Ÿšถโ€โ™‚๏ธโ€โžก๏ธ Walking or ๐Ÿƒโ€โ™‚๏ธโ€โžก๏ธ running distance +We compare the โšก๏ธ direct energy consumption with the energy consumption of someone ๐Ÿšถโ€โ™‚๏ธโ€โžก๏ธ walking or ๐Ÿƒโ€โ™‚๏ธโ€โžก๏ธ running. +From [runningtools.com](https://www.runningtools.com/energyusage.htm) we consider the following energy values per +physical activity (for someone weighing 70kg): +* ๐Ÿšถโ€โ™‚๏ธโ€โžก๏ธ walking: $ 196\ kJ/km $ (speed of $ 3\ km/h $) +* ๐Ÿƒโ€โ™‚๏ธโ€โžก๏ธ running: $ 294\ kJ/km $ (speed of $ 10\ km/h $) +We divide the request energy consumption by these values to compute the distance traveled. +#### ๐Ÿ”‹ Electric Vehicle distance +We compare the โšก๏ธ direct energy consumption with the energy consumer by a EV car. From +[selectra.info](https://selectra.info/energie/actualites/insolite/consommation-vehicules-electriques-france-2040) or +[tesla.com](https://www.tesla.com/fr_fr/support/power-consumption) we consider an average value of energy consumed per +kilometer of: $ 0.17\ kWh/km $. +We divide the request energy consumption by this value to compute the distance driven by an EV. +#### โฏ๏ธ Streaming time +We compare the ๐ŸŒ GHG emissions of the request and of streaming a video. From +[impactco2.fr](https://impactco2.fr/outils/comparateur?value=1&comparisons=streamingvideo), we consider that +$ 1\ kgCO2eq $ is equivalent to $ 15.6\ h $ of streaming. +We multiply that value by the GHG emissions of the request to get an equivalent in hours of video streaming. +### Scaled impacts +These equivalents are computed based on the request impacts scaled to a worldwide adoption use case. We imply that the +same request is done 1% of the planet everyday for 1 year, and then compute impact equivalents. +$$ +I_{scaled} = I_{request} * [1 \\% \ \text{of}\ 8B\ \text{people on earth}] * 365\ \text{days} +$$ +#### Number of ๐Ÿ’จ wind turbines or โ˜ข๏ธ nuclear plants +We compare the โšก๏ธ direct energy consumption (scaled) by the energy production of wind turbines and nuclear power +plants. From [ecologie.gouv.fr](https://www.ecologie.gouv.fr/eolien-terrestre) we consider that a $ 2\ MW $ wind +turbine produces $ 4.2\ GWh $ a year. And from [edf.fr](https://www.edf.fr/groupe-edf/espaces-dedies/jeunes-enseignants/pour-les-jeunes/lenergie-de-a-a-z/produire-de-lelectricite/le-nucleaire-en-chiffres) +we learn that a $ 900\ MW $ nuclear power plant produces $ 6\ TWh $ a year. +We divide the scaled energy consumption by these values to get the number of wind turbines or nuclear power plants +needed. +#### Multiplier of ๐Ÿ‡ฎ๐Ÿ‡ช Ireland electricity consumption +We compare the โšก๏ธ direct energy consumption (scaled) by the electricity consumption of Ireland per year. From +[wikipedia.org](https://en.wikipedia.org/wiki/List_of_countries_by_electricity_consumption) we consider the Ireland +electricity consumption to be $ 33\ TWh $ a year for a population of 5M. +We divide the scaled energy consumption by this value to get the equivalent number of "Ireland countries". +#### Number of โœˆ๏ธ Paris โ†” New York City flights +We compare the ๐ŸŒ GHG emissions (scaled) of the request and of a return flight Paris โ†” New York City. From +[impactco2.fr](https://impactco2.fr/outils/comparateur?value=1&comparisons=&equivalent=avion-pny) we consider that a +return flight Paris โ†’ New York City โ†’ Paris for one passenger emits $ 1,770\ kgCO2eq $ and we consider an overall +average load of 100 passengers per flight. +We divide the scaled GHG emissions by this value to get the equivalent number of return flights. + +#### If you are motivated to help us test and enhance this methodology [contact us](https://genai-impact.org/contact/)! ๐Ÿ’ช +""" + +CITATION_LABEL = "BibTeX citation for EcoLogits Calculator and the EcoLogits library:" +CITATION_TEXT = r"""@misc{ecologits-calculator, + author={Samuel Rincรฉ, Adrien Banse and Valentin Defour}, + title={EcoLogits Calculator}, + year={2025}, + howpublished= {\url{https://huggingface.co/spaces/genai-impact/ecologits-calculator}}, +} +@software{ecologits, + author = {Samuel Rincรฉ, Adrien Banse, Vinh Nguyen and Luc Berton}, + publisher = {GenAI Impact}, + title = {EcoLogits: track the energy consumption and environmental footprint of using generative AI models through APIs.}, +}""" + +LICENCE_TEXT = """

+ This work is licensed under + + CC BY-SA 4.0 + + + + +

""" \ No newline at end of file diff --git a/src/data/electricity_mix.csv b/src/data/electricity_mix.csv new file mode 100755 index 0000000000000000000000000000000000000000..416ad2525308ff910b9d38621c2db7f2bb250158 --- /dev/null +++ b/src/data/electricity_mix.csv @@ -0,0 +1,4 @@ +name,unit,source,WOR,EEE,ZWE,ZMB,ZAF,YEM,VNM,VEN,UZB,URY,USA,UKR,TZA,TWN,TTO,TUR,TUN,TKM,TJK,THA,TGO,SYR,SLV,SEN,SVK,SVN,SGP,SWE,SDN,SAU,RUS,SCG,ROU,QAT,PRY,PRT,POL,PAK,PHL,PER,PAN,OMN,NZL,NPL,NOR,NLD,NIC,NGA,NAM,MOZ,MYS,MEX,MLT,MNG,MMR,MKD,MDA,MAR,LBY,LVA,LUX,LTU,LKA,LBN,KAZ,KWT,KOR,PRK,KHM,KGZ,KEN,JPN,JOR,JAM,ITA,ISL,IRN,IRQ,IND,ISR,IRL,IDN,HUN,HTI,HRV,HND,HKG,GTM,GRC,GIB,GHA,GEO,GBR,GAB,FRA,FIN,ETH,ESP,ERI,EGY,EST,ECU,DZA,DOM,DNK,DEU,CZE,CYP,CUB,CRI,COL,CHN,CMR,CHL,CIV,CHE,COG,COD,CAN,BLR,BWA,BRA,BOL,BRN,BEN,BHR,BGR,BEL,BGD,BIH,AZE,AUS,AUT,ARG,AGO,ANT,ARM,ALB,ARE +adpe,kg รฉq. Sb,ADEME Base IMPACTS ยฎ,0.0000000737708,0.0000000642317,0.000000109502,0.000000162193,0.0000000862445,0.0000000163908,0.0000000945573,0.000000112811,0.000000103681,0.000000104586,0.0000000985548,0.0000000647907,0.000000132261,0.0000000578088,0.000000064552,0.0000000749765,0.0000000177021,0.000000131822,0.000000152621,0.0000000569593,0.000000134255,0.0000000268396,0.0000000472135,0.0000000470662,0.0000000606109,0.0000000992283,0.0000000198459,0.0000000777062,0.0000000955701,0.0000000134206,0.0000000960312,0.000000132772,0.0000000981761,0.00000001324,0.000000149181,0.0000000341863,0.000000101946,0.0000000842952,0.0000000595304,0.0000000952688,0.0000000790553,0.0000000374073,0.0000000720474,0.000000238273,0.000000127486,0.0000000329318,0.0000000414983,0.0000000621,0.000000128285,0.000000148382,0.000000044938,0.0000000578358,0.000000049475,0.000000176361,0.000000152699,0.000000119873,0.000000110674,0.0000000641089,0.0000000206592,0.000000153757,0.000000105692,0.0000000294596,0.0000000986932,0.0000000182134,0.000000135386,0.0000000141168,0.0000000518017,0.000000117457,0.0000000319202,0.000000181827,0.0000000958533,0.0000000596578,0.0000000147031,0.0000000196047,0.00000005439,0.0000000781905,0.0000000220304,0.0000000404306,0.000000100099,0.0000000610194,0.0000000219257,0.0000000610451,0.0000000644587,0.0000000937057,0.000000153989,0.0000000649373,0.0000000816213,0.0000000803251,0.0000000691645,0.0000000286211,0.000000156003,0.000000137999,0.0000000370973,0.000000113843,0.0000000485798,0.0000000805114,0.000000174161,0.0000000518326,0.0000000512406,0.000000033925,0.0000000990171,0.000000127168,0.0000000216438,0.0000000429285,0.0000000157411,0.0000000878733,0.0000000817565,0.0000000448771,0.0000000299542,0.0000000863908,0.000000122031,0.0000000851552,0.000000146313,0.000000105851,0.0000000949004,0.000000100467,0.000000265575,0.000000174647,0.0000000993179,0.0000000840478,0.0000000866014,0.00000010962,0.0000000969793,0.0000000185641,0.0000000239702,0.0000000135014,0.0000000823611,0.0000000337201,0.0000000394158,0.000000148007,0.000000092567,0.0000000790846,0.000000141124,0.0000000768612,0.000000124074,0.0000000449103,0.0000000854245,0.000000229556,0.0000000141548 +pe,MJ,ADPf / (1-%renewable_energy),9.988,12.873,16.122,1.798,11.732,16.250,11.238,15.163,17.367,107.570,11.358,12.936,9.305,11.439,14.289,16.150,12.902,23.300,19.165,10.414,21.978,16.989,13.012,14.516,11.680,12.146,10.477,11.026,29.629,14.058,13.200,14.242,15.585,11.916,0.020,14.153,13.178,16.175,11.120,8.211,16.364,22.306,24.731,0.396,4.952,8.511,24.696,11.279,468.150,0.206,12.268,11.775,19.374,15.114,14.132,19.120,18.429,11.702,19.116,8.249,10.128,21.043,12.116,12.341,13.260,12.753,10.199,32.793,34.655,15.380,68.996,10.718,13.677,14.799,12.656,0.013,15.022,20.372,20.363,10.023,10.706,11.603,11.784,20.167,18.548,15.762,,14.340,14.487,,10.097,10.425,13.579,28.341,11.289,11.275,36.133,12.090,13.289,10.195,16.334,20.908,16.376,12.412,16.824,16.260,12.517,13.118,17.317,45.996,7.312,14.119,10.807,11.348,14.783,11.782,34.147,0.097,11.987,13.194,19.642,9.031,11.587,15.689,14.337,14.036,14.375,10.776,12.935,21.705,12.831,16.908,11.036,10.049,16.972,,13.380,0.201,19.032 +gwp,kg รฉq. CO2,ADEME Base IMPACTS ยฎ,0.590478,0.509427,0.842811,0.0141304,1.17562,1.06777,0.555572,0.497373,0.81178,0.296953,0.67978,0.646745,0.475635,0.845351,0.933059,0.706988,0.80722,1.38296,0.0426743,0.646174,0.545455,1.08778,0.473128,1.1195,0.309341,0.498523,0.655825,0.0464664,1.12472,0.913677,0.66131,1.07808,0.664245,0.722125,0.241601,0.571172,1.15075,0.748727,0.761317,0.284364,0.53403,1.41292,0.293397,0.0841323,0.023754,0.544803,0.941626,0.693123,0.357253,0.00880732,0.832206,0.739214,1.31149,1.47192,0.48193,1.24074,1.04213,0.933694,1.35361,0.234273,0.490016,0.154229,0.709185,0.883627,1.128,0.885084,0.599585,0.797361,1.41054,0.156039,0.589603,0.540891,0.781372,1.07345,0.621329,0.0194609,0.930385,1.48728,1.58299,0.901842,0.648118,0.875394,0.541558,1.3858,0.535759,0.692837,0.95888,0.645801,1.13127,0.977477,0.540126,0.132046,0.602137,0.732511,0.0813225,0.322068,0.251299,0.467803,1.13153,0.587775,1.51492,0.627714,1.02318,0.909252,0.633534,0.657374,0.799077,0.978041,1.28325,0.224471,0.214014,1.05738,0.456622,0.574415,0.928583,0.0448568,0.904053,0.0613102,0.238191,0.795697,2.12572,0.278647,0.585131,0.997577,1.19128,0.804567,0.827087,0.256537,0.795168,1.56025,0.726126,1.12326,0.245573,0.476811,0.412014,1.13161,0.252002,0.0207635,1.09088 \ No newline at end of file diff --git a/src/electricity_mix.py b/src/electricity_mix.py old mode 100644 new mode 100755 index 77824fe03d7822d58603d0cd30637c802351f7a7..4395be484e7dbec8b723bb768e73c9e3acafa7fe --- a/src/electricity_mix.py +++ b/src/electricity_mix.py @@ -1,155 +1,175 @@ -from csv import DictReader - -PATH = "src/electricity_mix.csv" -COUNTRY_CODES = [ - ("๐ŸŒŽ World", "WOR"), - ("๐Ÿ‡ช๐Ÿ‡บ Europe", "EEE"), - ("๐Ÿ‡ฟ๐Ÿ‡ผ Zimbabwe", "ZWE"), - ("๐Ÿ‡ฟ๐Ÿ‡ฒ Zambia", "ZMB"), - ("๐Ÿ‡ฟ๐Ÿ‡ฆ South Africa", "ZAF"), - ("๐Ÿ‡พ๐Ÿ‡ช Yemen", "YEM"), - ("๐Ÿ‡ป๐Ÿ‡ณ Vietnam", "VNM"), - ("๐Ÿ‡ป๐Ÿ‡ช Venezuela", "VEN"), - ("๐Ÿ‡บ๐Ÿ‡ฟ Uzbekistan", "UZB"), - ("๐Ÿ‡บ๐Ÿ‡พ Uruguay", "URY"), - ("๐Ÿ‡บ๐Ÿ‡ธ United States", "USA"), - ("๐Ÿ‡บ๐Ÿ‡ฆ Ukraine", "UKR"), - ("๐Ÿ‡น๐Ÿ‡ฟ Tanzania", "TZA"), - ("๐Ÿ‡น๐Ÿ‡ผ Taiwan", "TWN"), - ("๐Ÿ‡น๐Ÿ‡น Trinidad and Tobago", "TTO"), - ("๐Ÿ‡น๐Ÿ‡ท Turkey", "TUR"), - ("๐Ÿ‡น๐Ÿ‡ณ Tunisia", "TUN"), - ("๐Ÿ‡น๐Ÿ‡ฒ Turkmenistan", "TKM"), - ("๐Ÿ‡น๐Ÿ‡ฏ Tajikistan", "TJK"), - ("๐Ÿ‡น๐Ÿ‡ญ Thailand", "THA"), - ("๐Ÿ‡น๐Ÿ‡ฌ Togo", "TGO"), - ("๐Ÿ‡ธ๐Ÿ‡พ Syrian Arab Republic", "SYR"), - ("๐Ÿ‡ธ๐Ÿ‡ป El Salvador", "SLV"), - ("๐Ÿ‡ธ๐Ÿ‡ณ Senegal", "SEN"), - ("๐Ÿ‡ธ๐Ÿ‡ฐ Slovak Republic", "SVK"), - ("๐Ÿ‡ธ๐Ÿ‡ฎ Slovenia", "SVN"), - ("๐Ÿ‡ธ๐Ÿ‡ฌ Singapore", "SGP"), - ("๐Ÿ‡ธ๐Ÿ‡ช Sweden", "SWE"), - ("๐Ÿ‡ธ๐Ÿ‡ฉ Sudan", "SDN"), - ("๐Ÿ‡ธ๐Ÿ‡ฆ Saudi Arabia", "SAU"), - ("๐Ÿ‡ท๐Ÿ‡บ Russian Federation", "RUS"), - ("๐Ÿ‡ท๐Ÿ‡ธ Serbia and Montenegro", "SCG"), - ("๐Ÿ‡ท๐Ÿ‡ด Romania", "ROU"), - ("๐Ÿ‡ถ๐Ÿ‡ฆ Qatar", "QAT"), - ("๐Ÿ‡ต๐Ÿ‡พ Paraguay", "PRY"), - ("๐Ÿ‡ต๐Ÿ‡น Portugal", "PRT"), - ("๐Ÿ‡ต๐Ÿ‡ฑ Poland", "POL"), - ("๐Ÿ‡ต๐Ÿ‡ฐ Pakistan", "PAK"), - ("๐Ÿ‡ต๐Ÿ‡ญ Philippines", "PHL"), - ("๐Ÿ‡ต๐Ÿ‡ช Peru", "PER"), - ("๐Ÿ‡ต๐Ÿ‡ฆ Panama", "PAN"), - ("๐Ÿ‡ด๐Ÿ‡ฒ Oman", "OMN"), - ("๐Ÿ‡ณ๐Ÿ‡ฟ New Zealand", "NZL"), - ("๐Ÿ‡ณ๐Ÿ‡ต Nepal", "NPL"), - ("๐Ÿ‡ณ๐Ÿ‡ด Norway", "NOR"), - ("๐Ÿ‡ณ๐Ÿ‡ฑ Netherlands", "NLD"), - ("๐Ÿ‡ณ๐Ÿ‡ฎ Nicaragua", "NIC"), - ("๐Ÿ‡ณ๐Ÿ‡ฌ Nigeria", "NGA"), - ("๐Ÿ‡ณ๐Ÿ‡ฆ Namibia", "NAM"), - ("๐Ÿ‡ฒ๐Ÿ‡ฟ Mozambique", "MOZ"), - ("๐Ÿ‡ฒ๐Ÿ‡พ Malaysia", "MYS"), - ("๐Ÿ‡ฒ๐Ÿ‡ฝ Mexico", "MEX"), - ("๐Ÿ‡ฒ๐Ÿ‡น Malta", "MLT"), - ("๐Ÿ‡ฒ๐Ÿ‡ณ Mongolia", "MNG"), - ("๐Ÿ‡ฒ๐Ÿ‡ฒ Myanmar", "MMR"), - ("๐Ÿ‡ฒ๐Ÿ‡ฐ North Macedonia", "MKD"), - ("๐Ÿ‡ฒ๐Ÿ‡ฉ Moldova", "MDA"), - ("๐Ÿ‡ฒ๐Ÿ‡ฆ Morocco", "MAR"), - ("๐Ÿ‡ฑ๐Ÿ‡พ Libya", "LBY"), - ("๐Ÿ‡ฑ๐Ÿ‡ป Latvia", "LVA"), - ("๐Ÿ‡ฑ๐Ÿ‡บ Luxembourg", "LUX"), - ("๐Ÿ‡ฑ๐Ÿ‡น Lithuania", "LTU"), - ("๐Ÿ‡ฑ๐Ÿ‡ฐ Sri Lanka", "LKA"), - ("๐Ÿ‡ฑ๐Ÿ‡ง Lebanon", "LBN"), - ("๐Ÿ‡ฐ๐Ÿ‡ฟ Kazakhstan", "KAZ"), - ("๐Ÿ‡ฐ๐Ÿ‡ผ Kuwait", "KWT"), - ("๐Ÿ‡ฐ๐Ÿ‡ท South Korea", "KOR"), - ("๐Ÿ‡ฐ๐Ÿ‡ต North Korea", "PRK"), - ("๐Ÿ‡ฐ๐Ÿ‡ญ Cambodia", "KHM"), - ("๐Ÿ‡ฐ๐Ÿ‡ฌ Kyrgyz Republic", "KGZ"), - ("๐Ÿ‡ฐ๐Ÿ‡ช Kenya", "KEN"), - ("๐Ÿ‡ฏ๐Ÿ‡ต Japan", "JPN"), - ("๐Ÿ‡ฏ๐Ÿ‡ด Jordan", "JOR"), - ("๐Ÿ‡ฏ๐Ÿ‡ฒ Jamaica", "JAM"), - ("๐Ÿ‡ฎ๐Ÿ‡น Italy", "ITA"), - ("๐Ÿ‡ฎ๐Ÿ‡ธ Iceland", "ISL"), - ("๐Ÿ‡ฎ๐Ÿ‡ท Iran", "IRN"), - ("๐Ÿ‡ฎ๐Ÿ‡ถ Iraq", "IRQ"), - ("๐Ÿ‡ฎ๐Ÿ‡ณ India", "IND"), - ("๐Ÿ‡ฎ๐Ÿ‡ฑ Israel", "ISR"), - ("๐Ÿ‡ฎ๐Ÿ‡ช Ireland", "IRL"), - ("๐Ÿ‡ฎ๐Ÿ‡ฉ Indonesia", "IDN"), - ("๐Ÿ‡ญ๐Ÿ‡บ Hungary", "HUN"), - ("๐Ÿ‡ญ๐Ÿ‡น Haiti", "HTI"), - ("๐Ÿ‡ญ๐Ÿ‡ท Croatia", "HRV"), - ("๐Ÿ‡ญ๐Ÿ‡ณ Honduras", "HND"), - ("๐Ÿ‡ญ๐Ÿ‡ฐ Hong Kong", "HKG"), - ("๐Ÿ‡ฌ๐Ÿ‡น Guatemala", "GTM"), - ("๐Ÿ‡ฌ๐Ÿ‡ท Greece", "GRC"), - ("๐Ÿ‡ฌ๐Ÿ‡ฎ Gibraltar", "GIB"), - ("๐Ÿ‡ฌ๐Ÿ‡ญ Ghana", "GHA"), - ("๐Ÿ‡ฌ๐Ÿ‡ช Georgia", "GEO"), - ("๐Ÿ‡ฌ๐Ÿ‡ง United Kingdom", "GBR"), - ("๐Ÿ‡ฌ๐Ÿ‡ฆ Gabon", "GAB"), - ("๐Ÿ‡ซ๐Ÿ‡ท France", "FRA"), - ("๐Ÿ‡ซ๐Ÿ‡ฎ Finland", "FIN"), - ("๐Ÿ‡ช๐Ÿ‡น Ethiopia", "ETH"), - ("๐Ÿ‡ช๐Ÿ‡ธ Spain", "ESP"), - ("๐Ÿ‡ช๐Ÿ‡ท Eritrea", "ERI"), - ("๐Ÿ‡ช๐Ÿ‡ฌ Egypt", "EGY"), - ("๐Ÿ‡ช๐Ÿ‡ช Estonia", "EST"), - ("๐Ÿ‡ช๐Ÿ‡จ Ecuador", "ECU"), - ("๐Ÿ‡ฉ๐Ÿ‡ฟ Algeria", "DZA"), - ("๐Ÿ‡ฉ๐Ÿ‡ด Dominican Republic", "DOM"), - ("๐Ÿ‡ฉ๐Ÿ‡ฐ Denmark", "DNK"), - ("๐Ÿ‡ฉ๐Ÿ‡ช Germany", "DEU"), - ("๐Ÿ‡จ๐Ÿ‡ฟ Czech Republic", "CZE"), - ("๐Ÿ‡จ๐Ÿ‡พ Cyprus", "CYP"), - ("๐Ÿ‡จ๐Ÿ‡บ Cuba", "CUB"), - ("๐Ÿ‡จ๐Ÿ‡ท Costa Rica", "CRI"), - ("๐Ÿ‡จ๐Ÿ‡ด Colombia", "COL"), - ("๐Ÿ‡จ๐Ÿ‡ณ China", "CHN"), - ("๐Ÿ‡จ๐Ÿ‡ฒ Cameroon", "CMR"), - ("๐Ÿ‡จ๐Ÿ‡ฑ Chile", "CHL"), - ("๐Ÿ‡จ๐Ÿ‡ฎ Cote d'Ivoire", "CIV"), - ("๐Ÿ‡จ๐Ÿ‡ญ Switzerland", "CHE"), - ("๐Ÿ‡จ๐Ÿ‡ฌ Congo", "COG"), - ("๐Ÿ‡จ๐Ÿ‡ฉ Democratic Republic of the Congo", "COD"), - ("๐Ÿ‡จ๐Ÿ‡ฆ Canada", "CAN"), - ("๐Ÿ‡ง๐Ÿ‡พ Belarus", "BLR"), - ("๐Ÿ‡ง๐Ÿ‡ผ Botswana", "BWA"), - ("๐Ÿ‡ง๐Ÿ‡ท Brazil", "BRA"), - ("๐Ÿ‡ง๐Ÿ‡ด Bolivia", "BOL"), - ("๐Ÿ‡ง๐Ÿ‡ณ Brunei", "BRN"), - ("๐Ÿ‡ง๐Ÿ‡ฏ Benin", "BEN"), - ("๐Ÿ‡ง๐Ÿ‡ญ Bahrain", "BHR"), - ("๐Ÿ‡ง๐Ÿ‡ฌ Bulgaria", "BGR"), - ("๐Ÿ‡ง๐Ÿ‡ช Belgium", "BEL"), - ("๐Ÿ‡ง๐Ÿ‡ฉ Bangladesh", "BGD"), - ("๐Ÿ‡ง๐Ÿ‡ฆ Bosnia and Herzegovina", "BIH"), - ("๐Ÿ‡ฆ๐Ÿ‡ฟ Azerbaijan", "AZE"), - ("๐Ÿ‡ฆ๐Ÿ‡บ Australia", "AUS"), - ("๐Ÿ‡ฆ๐Ÿ‡น Austria", "AUT"), - ("๐Ÿ‡ฆ๐Ÿ‡ท Argentina", "ARG"), - ("๐Ÿ‡ฆ๐Ÿ‡ด Angola", "AGO"), - ("๐Ÿ‡ฆ Netherlands Antilles", "ANT"), - ("๐Ÿ‡ฆ๐Ÿ‡ฒ Armenia", "ARM"), - ("๐Ÿ‡ฆ๐Ÿ‡ฑ Albania", "ALB"), - ("๐Ÿ‡ฆ๐Ÿ‡ช United Arab Emirates", "ARE") -] - - -def find_electricity_mix(code: str): - # TODO: Maybe more optimal to construct database at the beginning of the app - # in the same fashion as find_model - res = [] - with open(PATH) as fd: - csv = DictReader(fd) - for row in csv: - res += [float(row[code])] - return res +from csv import DictReader +import pandas as pd + +PATH = "src/data/electricity_mix.csv" + +COUNTRY_CODES = [ + ("๐ŸŒŽ World", "WOR"), + ("๐Ÿ‡ช๐Ÿ‡บ Europe", "EEE"), + ("๐Ÿ‡ฟ๐Ÿ‡ผ Zimbabwe", "ZWE"), + ("๐Ÿ‡ฟ๐Ÿ‡ฒ Zambia", "ZMB"), + ("๐Ÿ‡ฟ๐Ÿ‡ฆ South Africa", "ZAF"), + ("๐Ÿ‡พ๐Ÿ‡ช Yemen", "YEM"), + ("๐Ÿ‡ป๐Ÿ‡ณ Vietnam", "VNM"), + ("๐Ÿ‡ป๐Ÿ‡ช Venezuela", "VEN"), + ("๐Ÿ‡บ๐Ÿ‡ฟ Uzbekistan", "UZB"), + ("๐Ÿ‡บ๐Ÿ‡พ Uruguay", "URY"), + ("๐Ÿ‡บ๐Ÿ‡ธ United States", "USA"), + ("๐Ÿ‡บ๐Ÿ‡ฆ Ukraine", "UKR"), + ("๐Ÿ‡น๐Ÿ‡ฟ Tanzania", "TZA"), + ("๐Ÿ‡น๐Ÿ‡ผ Taiwan", "TWN"), + ("๐Ÿ‡น๐Ÿ‡น Trinidad and Tobago", "TTO"), + ("๐Ÿ‡น๐Ÿ‡ท Turkey", "TUR"), + ("๐Ÿ‡น๐Ÿ‡ณ Tunisia", "TUN"), + ("๐Ÿ‡น๐Ÿ‡ฒ Turkmenistan", "TKM"), + ("๐Ÿ‡น๐Ÿ‡ฏ Tajikistan", "TJK"), + ("๐Ÿ‡น๐Ÿ‡ญ Thailand", "THA"), + ("๐Ÿ‡น๐Ÿ‡ฌ Togo", "TGO"), + ("๐Ÿ‡ธ๐Ÿ‡พ Syrian Arab Republic", "SYR"), + ("๐Ÿ‡ธ๐Ÿ‡ป El Salvador", "SLV"), + ("๐Ÿ‡ธ๐Ÿ‡ณ Senegal", "SEN"), + ("๐Ÿ‡ธ๐Ÿ‡ฐ Slovak Republic", "SVK"), + ("๐Ÿ‡ธ๐Ÿ‡ฎ Slovenia", "SVN"), + ("๐Ÿ‡ธ๐Ÿ‡ฌ Singapore", "SGP"), + ("๐Ÿ‡ธ๐Ÿ‡ช Sweden", "SWE"), + ("๐Ÿ‡ธ๐Ÿ‡ฉ Sudan", "SDN"), + ("๐Ÿ‡ธ๐Ÿ‡ฆ Saudi Arabia", "SAU"), + ("๐Ÿ‡ท๐Ÿ‡บ Russian Federation", "RUS"), + ("๐Ÿ‡ท๐Ÿ‡ธ Serbia and Montenegro", "SCG"), + ("๐Ÿ‡ท๐Ÿ‡ด Romania", "ROU"), + ("๐Ÿ‡ถ๐Ÿ‡ฆ Qatar", "QAT"), + ("๐Ÿ‡ต๐Ÿ‡พ Paraguay", "PRY"), + ("๐Ÿ‡ต๐Ÿ‡น Portugal", "PRT"), + ("๐Ÿ‡ต๐Ÿ‡ฑ Poland", "POL"), + ("๐Ÿ‡ต๐Ÿ‡ฐ Pakistan", "PAK"), + ("๐Ÿ‡ต๐Ÿ‡ญ Philippines", "PHL"), + ("๐Ÿ‡ต๐Ÿ‡ช Peru", "PER"), + ("๐Ÿ‡ต๐Ÿ‡ฆ Panama", "PAN"), + ("๐Ÿ‡ด๐Ÿ‡ฒ Oman", "OMN"), + ("๐Ÿ‡ณ๐Ÿ‡ฟ New Zealand", "NZL"), + ("๐Ÿ‡ณ๐Ÿ‡ต Nepal", "NPL"), + ("๐Ÿ‡ณ๐Ÿ‡ด Norway", "NOR"), + ("๐Ÿ‡ณ๐Ÿ‡ฑ Netherlands", "NLD"), + ("๐Ÿ‡ณ๐Ÿ‡ฎ Nicaragua", "NIC"), + ("๐Ÿ‡ณ๐Ÿ‡ฌ Nigeria", "NGA"), + ("๐Ÿ‡ณ๐Ÿ‡ฆ Namibia", "NAM"), + ("๐Ÿ‡ฒ๐Ÿ‡ฟ Mozambique", "MOZ"), + ("๐Ÿ‡ฒ๐Ÿ‡พ Malaysia", "MYS"), + ("๐Ÿ‡ฒ๐Ÿ‡ฝ Mexico", "MEX"), + ("๐Ÿ‡ฒ๐Ÿ‡น Malta", "MLT"), + ("๐Ÿ‡ฒ๐Ÿ‡ณ Mongolia", "MNG"), + ("๐Ÿ‡ฒ๐Ÿ‡ฒ Myanmar", "MMR"), + ("๐Ÿ‡ฒ๐Ÿ‡ฐ North Macedonia", "MKD"), + ("๐Ÿ‡ฒ๐Ÿ‡ฉ Moldova", "MDA"), + ("๐Ÿ‡ฒ๐Ÿ‡ฆ Morocco", "MAR"), + ("๐Ÿ‡ฑ๐Ÿ‡พ Libya", "LBY"), + ("๐Ÿ‡ฑ๐Ÿ‡ป Latvia", "LVA"), + ("๐Ÿ‡ฑ๐Ÿ‡บ Luxembourg", "LUX"), + ("๐Ÿ‡ฑ๐Ÿ‡น Lithuania", "LTU"), + ("๐Ÿ‡ฑ๐Ÿ‡ฐ Sri Lanka", "LKA"), + ("๐Ÿ‡ฑ๐Ÿ‡ง Lebanon", "LBN"), + ("๐Ÿ‡ฐ๐Ÿ‡ฟ Kazakhstan", "KAZ"), + ("๐Ÿ‡ฐ๐Ÿ‡ผ Kuwait", "KWT"), + ("๐Ÿ‡ฐ๐Ÿ‡ท South Korea", "KOR"), + ("๐Ÿ‡ฐ๐Ÿ‡ต North Korea", "PRK"), + ("๐Ÿ‡ฐ๐Ÿ‡ญ Cambodia", "KHM"), + ("๐Ÿ‡ฐ๐Ÿ‡ฌ Kyrgyz Republic", "KGZ"), + ("๐Ÿ‡ฐ๐Ÿ‡ช Kenya", "KEN"), + ("๐Ÿ‡ฏ๐Ÿ‡ต Japan", "JPN"), + ("๐Ÿ‡ฏ๐Ÿ‡ด Jordan", "JOR"), + ("๐Ÿ‡ฏ๐Ÿ‡ฒ Jamaica", "JAM"), + ("๐Ÿ‡ฎ๐Ÿ‡น Italy", "ITA"), + ("๐Ÿ‡ฎ๐Ÿ‡ธ Iceland", "ISL"), + ("๐Ÿ‡ฎ๐Ÿ‡ท Iran", "IRN"), + ("๐Ÿ‡ฎ๐Ÿ‡ถ Iraq", "IRQ"), + ("๐Ÿ‡ฎ๐Ÿ‡ณ India", "IND"), + ("๐Ÿ‡ฎ๐Ÿ‡ฑ Israel", "ISR"), + ("๐Ÿ‡ฎ๐Ÿ‡ช Ireland", "IRL"), + ("๐Ÿ‡ฎ๐Ÿ‡ฉ Indonesia", "IDN"), + ("๐Ÿ‡ญ๐Ÿ‡บ Hungary", "HUN"), + ("๐Ÿ‡ญ๐Ÿ‡น Haiti", "HTI"), + ("๐Ÿ‡ญ๐Ÿ‡ท Croatia", "HRV"), + ("๐Ÿ‡ญ๐Ÿ‡ณ Honduras", "HND"), + ("๐Ÿ‡ญ๐Ÿ‡ฐ Hong Kong", "HKG"), + ("๐Ÿ‡ฌ๐Ÿ‡น Guatemala", "GTM"), + ("๐Ÿ‡ฌ๐Ÿ‡ท Greece", "GRC"), + ("๐Ÿ‡ฌ๐Ÿ‡ฎ Gibraltar", "GIB"), + ("๐Ÿ‡ฌ๐Ÿ‡ญ Ghana", "GHA"), + ("๐Ÿ‡ฌ๐Ÿ‡ช Georgia", "GEO"), + ("๐Ÿ‡ฌ๐Ÿ‡ง United Kingdom", "GBR"), + ("๐Ÿ‡ฌ๐Ÿ‡ฆ Gabon", "GAB"), + ("๐Ÿ‡ซ๐Ÿ‡ท France", "FRA"), + ("๐Ÿ‡ซ๐Ÿ‡ฎ Finland", "FIN"), + ("๐Ÿ‡ช๐Ÿ‡น Ethiopia", "ETH"), + ("๐Ÿ‡ช๐Ÿ‡ธ Spain", "ESP"), + ("๐Ÿ‡ช๐Ÿ‡ท Eritrea", "ERI"), + ("๐Ÿ‡ช๐Ÿ‡ฌ Egypt", "EGY"), + ("๐Ÿ‡ช๐Ÿ‡ช Estonia", "EST"), + ("๐Ÿ‡ช๐Ÿ‡จ Ecuador", "ECU"), + ("๐Ÿ‡ฉ๐Ÿ‡ฟ Algeria", "DZA"), + ("๐Ÿ‡ฉ๐Ÿ‡ด Dominican Republic", "DOM"), + ("๐Ÿ‡ฉ๐Ÿ‡ฐ Denmark", "DNK"), + ("๐Ÿ‡ฉ๐Ÿ‡ช Germany", "DEU"), + ("๐Ÿ‡จ๐Ÿ‡ฟ Czech Republic", "CZE"), + ("๐Ÿ‡จ๐Ÿ‡พ Cyprus", "CYP"), + ("๐Ÿ‡จ๐Ÿ‡บ Cuba", "CUB"), + ("๐Ÿ‡จ๐Ÿ‡ท Costa Rica", "CRI"), + ("๐Ÿ‡จ๐Ÿ‡ด Colombia", "COL"), + ("๐Ÿ‡จ๐Ÿ‡ณ China", "CHN"), + ("๐Ÿ‡จ๐Ÿ‡ฒ Cameroon", "CMR"), + ("๐Ÿ‡จ๐Ÿ‡ฑ Chile", "CHL"), + ("๐Ÿ‡จ๐Ÿ‡ฎ Cote d'Ivoire", "CIV"), + ("๐Ÿ‡จ๐Ÿ‡ญ Switzerland", "CHE"), + ("๐Ÿ‡จ๐Ÿ‡ฌ Congo", "COG"), + ("๐Ÿ‡จ๐Ÿ‡ฉ Democratic Republic of the Congo", "COD"), + ("๐Ÿ‡จ๐Ÿ‡ฆ Canada", "CAN"), + ("๐Ÿ‡ง๐Ÿ‡พ Belarus", "BLR"), + ("๐Ÿ‡ง๐Ÿ‡ผ Botswana", "BWA"), + ("๐Ÿ‡ง๐Ÿ‡ท Brazil", "BRA"), + ("๐Ÿ‡ง๐Ÿ‡ด Bolivia", "BOL"), + ("๐Ÿ‡ง๐Ÿ‡ณ Brunei", "BRN"), + ("๐Ÿ‡ง๐Ÿ‡ฏ Benin", "BEN"), + ("๐Ÿ‡ง๐Ÿ‡ญ Bahrain", "BHR"), + ("๐Ÿ‡ง๐Ÿ‡ฌ Bulgaria", "BGR"), + ("๐Ÿ‡ง๐Ÿ‡ช Belgium", "BEL"), + ("๐Ÿ‡ง๐Ÿ‡ฉ Bangladesh", "BGD"), + ("๐Ÿ‡ง๐Ÿ‡ฆ Bosnia and Herzegovina", "BIH"), + ("๐Ÿ‡ฆ๐Ÿ‡ฟ Azerbaijan", "AZE"), + ("๐Ÿ‡ฆ๐Ÿ‡บ Australia", "AUS"), + ("๐Ÿ‡ฆ๐Ÿ‡น Austria", "AUT"), + ("๐Ÿ‡ฆ๐Ÿ‡ท Argentina", "ARG"), + ("๐Ÿ‡ฆ๐Ÿ‡ด Angola", "AGO"), + ("๐Ÿ‡ฆ Netherlands Antilles", "ANT"), + ("๐Ÿ‡ฆ๐Ÿ‡ฒ Armenia", "ARM"), + ("๐Ÿ‡ฆ๐Ÿ‡ฑ Albania", "ALB"), + ("๐Ÿ‡ฆ๐Ÿ‡ช United Arab Emirates", "ARE") +] + + +def find_electricity_mix(code: str): + # TODO: Maybe more optimal to construct database at the beginning of the app + # in the same fashion as find_model + res = [] + with open(PATH) as fd: + csv = DictReader(fd) + for row in csv: + res += [float(row[code])] + return res + +def dataframe_electricity_mix(countries: list): + + df = pd.read_csv('src/data/electricity_mix.csv') + df['name_unit'] = df['name'] + ' (' + df['unit'] + ')' + df = df[['name_unit'] + [x[1] for x in COUNTRY_CODES if x[0] in countries]] + + df_melted = df.melt( + id_vars=['name_unit'], + value_vars=[x[1] for x in COUNTRY_CODES if x[0] in countries], + var_name='country', + value_name='value') + + df = df_melted.pivot(columns='name_unit', + index='country', + values='value') + + return df \ No newline at end of file diff --git a/src/expert.py b/src/expert.py new file mode 100755 index 0000000000000000000000000000000000000000..700fb629a20143b0d6a8830c01a82a85f5ece698 --- /dev/null +++ b/src/expert.py @@ -0,0 +1,156 @@ +import streamlit as st +import pandas as pd +from ecologits.impacts.llm import compute_llm_impacts + +from src.utils import format_impacts, average_range_impacts, format_impacts_expert, model_active_params_fn, model_total_params_fn +from src.impacts import display_impacts +#from src.constants import PROVIDERS, MODELS +from src.electricity_mix import COUNTRY_CODES, find_electricity_mix, dataframe_electricity_mix +from ecologits.model_repository import models + +import plotly.express as px + +def reset_model(): + model = 'CUSTOM' + +def expert_mode(): + + st.markdown("### ๐Ÿค“ Expert mode") + + with st.container(border = True): + + ########## Model info ########## + + # col1, col2, col3 = st.columns(3) + + # with col1: + # provider = st.selectbox(label = 'Provider expert', + # options = [x[0] for x in PROVIDERS], + # index = 0) + # provider = [x[1] for x in PROVIDERS if x[0] == provider][0] + # if 'huggingface_hub' in provider: + # provider = 'huggingface_hub' + + # with col2: + # model = st.selectbox('Model expert', [x[0] for x in MODELS if provider in x[1]]) + # model = [x[1] for x in MODELS if x[0] == model][0].split('/', 1)[1] + + ########## Model parameters ########## + + col11, col22, col33 = st.columns(3) + + with col11: + # st.write(provider, model) + # st.write(models.find_model(provider, model)) + # st.write(model_active_params_fn(provider, model, 45)) + active_params = st.number_input('Active parameters (B)', 0, None, 45) + + with col22: + total_params = st.number_input('Total parameters (B)', 0, None, 45) + + with col33: + output_tokens = st.number_input('Output completion tokens', 100) + + ########## Electricity mix ########## + + location = st.selectbox('Location', [x[0] for x in COUNTRY_CODES]) + + col4, col5, col6 = st.columns(3) + + with col4: + mix_gwp = st.number_input('Electricity mix - GHG emissions [kgCO2eq / kWh]', find_electricity_mix([x[1] for x in COUNTRY_CODES if x[0] ==location][0])[2], format="%0.6f") + #disp_ranges = st.toggle('Display impact ranges', False) + with col5: + mix_adpe = st.number_input('Electricity mix - Abiotic resources [kgSbeq / kWh]', find_electricity_mix([x[1] for x in COUNTRY_CODES if x[0] ==location][0])[0], format="%0.13f") + with col6: + mix_pe = st.number_input('Electricity mix - Primary energy [MJ / kWh]', find_electricity_mix([x[1] for x in COUNTRY_CODES if x[0] ==location][0])[1], format="%0.3f") + + impacts = compute_llm_impacts(model_active_parameter_count=active_params, + model_total_parameter_count=total_params, + output_token_count=output_tokens, + request_latency=100000, + if_electricity_mix_gwp=mix_gwp, + if_electricity_mix_adpe=mix_adpe, + if_electricity_mix_pe=mix_pe + ) + + impacts, usage, embodied = format_impacts(impacts) + + with st.container(border = True): + + st.markdown('

Environmental Impacts

', unsafe_allow_html = True) + + display_impacts(impacts) + + with st.expander('โš–๏ธ Usage vs Embodied'): + + st.markdown('

Embodied vs Usage comparison

', unsafe_allow_html = True) + + st.markdown('The usage impacts account for the electricity consumption of the model while the embodied impacts account for resource extraction (e.g., minerals and metals), manufacturing, and transportation of the hardware.') + + col_ghg_comparison, col_adpe_comparison, col_pe_comparison = st.columns(3) + + with col_ghg_comparison: + fig_gwp = px.pie( + values = [average_range_impacts(usage.gwp.value), average_range_impacts(embodied.gwp.value)], + names = ['usage', 'embodied'], + title = 'GHG emissions', + color_discrete_sequence=["#636EFA", "#00CC96"], + width = 100 + ) + fig_gwp.update_layout(showlegend=False, title_x=0.5) + + st.plotly_chart(fig_gwp) + + with col_adpe_comparison: + fig_adpe = px.pie( + values = [average_range_impacts(usage.adpe.value), average_range_impacts(embodied.adpe.value)], + names = ['usage', 'embodied'], + title = 'Abiotic depletion', + color_discrete_sequence=["#00CC96","#636EFA"], + width = 100) + fig_adpe.update_layout( + showlegend=True, + legend=dict(yanchor="bottom", x = 0.35, y = -0.1), + title_x=0.5) + + st.plotly_chart(fig_adpe) + + with col_pe_comparison: + fig_pe = px.pie( + values = [average_range_impacts(usage.pe.value), average_range_impacts(embodied.pe.value)], + names = ['usage', 'embodied'], + title = 'Primary energy', + color_discrete_sequence=["#636EFA", "#00CC96"], + width = 100) + fig_pe.update_layout(showlegend=False, title_x=0.5) + + st.plotly_chart(fig_pe) + + with st.expander('๐ŸŒ๏ธ Location impact'): + + st.markdown('

How can location impact the footprint ?

', unsafe_allow_html = True) + + countries_to_compare = st.multiselect( + label = 'Countries to compare', + options = [x[0] for x in COUNTRY_CODES], + default = ["๐Ÿ‡ซ๐Ÿ‡ท France", "๐Ÿ‡บ๐Ÿ‡ธ United States", "๐Ÿ‡จ๐Ÿ‡ณ China"] + ) + + try: + + df = dataframe_electricity_mix(countries_to_compare) + + impact_type = st.selectbox( + label='Select an impact type to compare', + options=[x for x in df.columns if x!='country'], + index=1) + + df.sort_values(by = impact_type, inplace = True) + + fig_2 = px.bar(df, x = df.index, y = impact_type, text = impact_type, color = impact_type) + st.plotly_chart(fig_2) + + except: + + st.warning("Can't display chart with no values.") \ No newline at end of file diff --git a/src/impacts.py b/src/impacts.py new file mode 100755 index 0000000000000000000000000000000000000000..4040e86409f3dd3978d752e303f48691bfe0a47c --- /dev/null +++ b/src/impacts.py @@ -0,0 +1,116 @@ +import streamlit as st +import ecologits +from src.utils import ( + format_energy_eq_electric_vehicle, + format_energy_eq_electricity_consumption_ireland, + format_energy_eq_electricity_production, + format_energy_eq_physical_activity, + format_gwp_eq_airplane_paris_nyc, + format_gwp_eq_streaming, + PhysicalActivity, + EnergyProduction +) + +############################################################################################################ + +def get_impacts(model, active_params, total_params, mix_ghg, mix_adpe, mix_pe): + + return 1 + +############################################################################################################ + + +def display_impacts(impacts): + + st.divider() + + col_energy, col_ghg, col_adpe, col_pe, col_water = st.columns(5) + + with col_energy: + st.markdown('

โšก๏ธ Energy

', unsafe_allow_html = True) + st.latex(f'\Large {impacts.energy.magnitude:.3g} \ \large {impacts.energy.units}') + st.markdown(f'

Evaluates the electricity consumption

', unsafe_allow_html = True) + + with col_ghg: + st.markdown('

๐ŸŒ๏ธ GHG Emissions

', unsafe_allow_html = True) + st.latex(f'\Large {impacts.gwp.magnitude:.3g} \ \large {impacts.gwp.units}') + st.markdown(f'

Evaluates the effect on global warming

', unsafe_allow_html = True) + + with col_adpe: + st.markdown('

๐Ÿชจ Abiotic Resources

', unsafe_allow_html = True) + st.latex(f'\Large {impacts.adpe.magnitude:.3g} \ \large {impacts.adpe.units}') + st.markdown(f'

Evaluates the use of metals and minerals

', unsafe_allow_html = True) + + with col_pe: + st.markdown('

โ›ฝ๏ธ Primary Energy

', unsafe_allow_html = True) + st.latex(f'\Large {impacts.pe.magnitude:.3g} \ \large {impacts.pe.units}') + st.markdown(f'

Evaluates the use of energy resources

', unsafe_allow_html = True) + + with col_water: + st.markdown('

๐Ÿšฐ Water

', unsafe_allow_html = True) + st.latex('\Large Upcoming...') + st.markdown(f'

Evaluates the use of water

', unsafe_allow_html = True) + +############################################################################################################ + +def display_equivalent(impacts): + + st.divider() + + ev_eq = format_energy_eq_electric_vehicle(impacts.energy) + + streaming_eq = format_gwp_eq_streaming(impacts.gwp) + + col1, col2, col3 = st.columns(3) + + with col1: + physical_activity, distance = format_energy_eq_physical_activity(impacts.energy) + if physical_activity == PhysicalActivity.WALKING: + physical_activity = "๐Ÿšถ " + physical_activity.capitalize() + if physical_activity == PhysicalActivity.RUNNING: + physical_activity = "๐Ÿƒ " + physical_activity.capitalize() + + st.markdown(f'

{physical_activity}

', unsafe_allow_html = True) + st.latex(f'\Large {distance.magnitude:.3g} \ \large {distance.units}') + st.markdown(f'

Based on energy consumption

', unsafe_allow_html = True) + + with col2: + ev_eq = format_energy_eq_electric_vehicle(impacts.energy) + st.markdown(f'

๐Ÿ”‹ Electric Vehicle

', unsafe_allow_html = True) + st.latex(f'\Large {ev_eq.magnitude:.3g} \ \large {ev_eq.units}') + st.markdown(f'

Based on energy consumption

', unsafe_allow_html = True) + + with col3: + streaming_eq = format_gwp_eq_streaming(impacts.gwp) + st.markdown(f'

โฏ๏ธ Streaming

', unsafe_allow_html = True) + st.latex(f'\Large {streaming_eq.magnitude:.3g} \ \large {streaming_eq.units}') + st.markdown(f'

Based on GHG emissions

', unsafe_allow_html = True) + + st.divider() + + st.markdown('

What if 1% of the planet does this request everyday for 1 year ?

', unsafe_allow_html = True) + st.markdown('

If this use case is largely deployed around the world, the equivalent impacts would be the impacts of this request x 1% of 8 billion people x 365 days in a year.

', unsafe_allow_html = True) + + col4, col5, col6 = st.columns(3) + + with col4: + + electricity_production, count = format_energy_eq_electricity_production(impacts.energy) + if electricity_production == EnergyProduction.NUCLEAR: + emoji = "โ˜ข๏ธ" + name = "Nuclear power plants" + if electricity_production == EnergyProduction.WIND: + emoji = "๐Ÿ’จ๏ธ " + name = "Wind turbines" + st.markdown(f'

{emoji} {count.magnitude:.0f} {name} (yearly)

', unsafe_allow_html = True) + st.markdown(f'

Based on energy consumption

', unsafe_allow_html = True) + + with col5: + ireland_count = format_energy_eq_electricity_consumption_ireland(impacts.energy) + st.markdown(f'

๐Ÿ‡ฎ๐Ÿ‡ช {ireland_count.magnitude:.3f} x Ireland (yearly โšก๏ธ cons.)

', unsafe_allow_html = True) + st.markdown(f'

Based on energy consumption

', unsafe_allow_html = True) + + with col6: + paris_nyc_airplane = format_gwp_eq_airplane_paris_nyc(impacts.gwp) + st.markdown(f'

โœˆ๏ธ {round(paris_nyc_airplane.magnitude):,} Paris โ†” NYC

', unsafe_allow_html = True) + st.markdown(f'

Based on GHG emissions

', unsafe_allow_html = True) \ No newline at end of file diff --git a/src/models.py b/src/models.py new file mode 100755 index 0000000000000000000000000000000000000000..956903fd3d0df69f6d8e4ec9895f25cd153a2a01 --- /dev/null +++ b/src/models.py @@ -0,0 +1,46 @@ +import requests +import json +import pandas as pd +from src.constants import MODEL_REPOSITORY_URL + +def clean_models_data(df): + + dict_providers = { + 'google': 'Google', + 'mistralai': 'MistralAI', + 'meta-llama': 'Meta', + 'openai': 'OpenAI', + 'anthropic': 'Anthropic', + 'cohere': 'Cohere', + 'microsoft': 'Microsoft', + 'mistral-community': 'Mistral Community', + 'databricks': 'Databricks' + } + + df.drop('type', axis=1, inplace=True) + + df.loc[df['name'].str.contains('/'), 'name_clean'] = df.loc[df['name'].str.contains('/'), 'name'].str.split('/').str[1] + df['name_clean'] = df['name_clean'].fillna(df['name']) + df['name_clean'] = df['name_clean'].replace({'-': ' '}, regex = True) + + df.loc[df['provider'] == 'huggingface_hub', 'provider_clean'] = df.loc[df['provider'] == 'huggingface_hub', 'name'].str.split('/').str[0] + df['provider_clean'] = df['provider_clean'].fillna(df['provider']) + df['provider_clean'] = df['provider_clean'].replace(dict_providers, regex = True) + + df['architecture_type'] = df['architecture'].apply(lambda x: x['type']) + df['architecture_parameters'] = df['architecture'].apply(lambda x: x['parameters']) + + df['warnings'] = df['warnings'].apply(lambda x: ', '.join(x) if x else None).fillna('none') + df['warning_arch'] = df['warnings'].apply(lambda x: 'model-arch-not-released' in x) + df['warning_multi_modal'] = df['warnings'].apply(lambda x: 'model-arch-multimodal' in x) + + return df[['provider', 'provider_clean', 'name', 'name_clean', 'architecture_type', 'architecture_parameters', 'warning_arch', 'warning_multi_modal']] + +def load_models(): + + resp = requests.get(MODEL_REPOSITORY_URL) + data = json.loads(resp.text) + df = pd.DataFrame(data['models']) + + return clean_models_data(df) + diff --git a/src/scrapper.py b/src/scrapper.py deleted file mode 100644 index e85b2e51d9b335dcf233b820256ad66bb0fed46e..0000000000000000000000000000000000000000 --- a/src/scrapper.py +++ /dev/null @@ -1,33 +0,0 @@ -import requests -from bs4 import BeautifulSoup -import tiktoken - -tokenizer = tiktoken.get_encoding('cl100k_base') - -def process_input(text): - - r = requests.get(text, verify=False) - - soup = BeautifulSoup(r.text, "html.parser") - print(soup) - list_text = str(soup).split('parts":["') - #print(list_text) - s = '' - for item in list_text[1:int(len(list_text)/2)]: - if list_text.index(item)%2 == 1: - s = s + item.split('"]')[0] - - amout_token = tiktoken_len(s) - - return amout_token - -def tiktoken_len(text): - tokens = tokenizer.encode( - text, - disallowed_special=() - ) - return len(tokens) - -answer = process_input('https://chatgpt.com/share/6737b9b5-56fc-8002-a212-35339f5b1d5a') - -print(answer) \ No newline at end of file diff --git a/src/utils.py b/src/utils.py old mode 100644 new mode 100755 index ee510314985d370438cba4d1073563664b3cfbc4..af4187d59db6f21451bd8b4a921a5fee808f99e6 --- a/src/utils.py +++ b/src/utils.py @@ -1,197 +1,262 @@ -from dataclasses import dataclass -from enum import Enum - -import pandas as pd - -from ecologits.impacts.modeling import Impacts, Energy, GWP, ADPe, PE -from ecologits.tracers.utils import llm_impacts, _avg -from pint import UnitRegistry, Quantity - - -u = UnitRegistry() -u.define('Wh = watt_hour') -u.define('kWh = kilowatt_hour') -u.define('MWh = megawatt_hour') -u.define('GWh = gigawatt_hour') -u.define('TWh = terawatt_hour') -u.define('gCO2eq = gram') -u.define('kgCO2eq = kilogram') -u.define('tCO2eq = metricton') -u.define('kgSbeq = kilogram') -u.define('kJ = kilojoule') -u.define('MJ = megajoule') -u.define('m = meter') -u.define('km = kilometer') -u.define('s = second') -u.define('min = minute') -u.define('h = hour') -q = u.Quantity - - -@dataclass -class QImpacts: - energy: Quantity - gwp: Quantity - adpe: Quantity - pe: Quantity - - -class PhysicalActivity(str, Enum): - RUNNING = "running" - WALKING = "walking" - - -class EnergyProduction(str, Enum): - NUCLEAR = "nuclear" - WIND = "wind" - - -COUNTRIES = [ - ("cook_islands", 38.81, 9_556), - ("tonga", 51.15, 104_490), - ("comoros", 100, 821_632), - ("samoa", 100, 821_632), -] - -def df_elec_mix_for_plot(): - return pd.DataFrame({ - 'country': ['Sweden', 'France', 'Canada', 'USA', 'China', 'Australia', 'India'], - 'electricity_mix': [46, 81, 238, 679, 1057, 1123, 1583] - }) - -# From https://www.runningtools.com/energyusage.htm -RUNNING_ENERGY_EQ = q("294 kJ / km") # running 1 km at 10 km/h with a weight of 70 kg -WALKING_ENERGY_EQ = q("196 kJ / km") # walking 1 km at 3 km/h with a weight of 70 kg - -# From https://selectra.info/energie/actualites/insolite/consommation-vehicules-electriques-france-2040 -# and https://www.tesla.com/fr_fr/support/power-consumption -EV_ENERGY_EQ = q("0.17 kWh / km") - -# From https://impactco2.fr/outils/comparateur?value=1&comparisons=streamingvideo -STREAMING_GWP_EQ = q("15.6 h / kgCO2eq") - -# From https://ourworldindata.org/population-growth -ONE_PERCENT_WORLD_POPULATION = 80_000_000 - -DAYS_IN_YEAR = 365 - -# For a 900 MW nuclear plant -> 500 000 MWh / month -# From https://www.edf.fr/groupe-edf/espaces-dedies/jeunes-enseignants/pour-les-jeunes/lenergie-de-a-a-z/produire-de-lelectricite/le-nucleaire-en-chiffres -YEARLY_NUCLEAR_ENERGY_EQ = q("6 TWh") - -# For a 2MW wind turbine -# https://www.ecologie.gouv.fr/eolien-terrestre -YEARLY_WIND_ENERGY_EQ = q("4.2 GWh") - -# Ireland yearly electricity consumption -# From https://en.wikipedia.org/wiki/List_of_countries_by_electricity_consumption -YEARLY_IRELAND_ELECTRICITY_CONSUMPTION = q("33 TWh") -IRELAND_POPULATION_MILLION = 5 - -# From https://impactco2.fr/outils/comparateur?value=1&comparisons=&equivalent=avion-pny -AIRPLANE_PARIS_NYC_GWP_EQ = q("177000 kgCO2eq") - - -def format_energy(energy: Energy) -> Quantity: - val = q(energy.value, energy.unit) - if val < q("1 kWh"): - val = val.to("Wh") - return val - - -def format_gwp(gwp: GWP) -> Quantity: - val = q(gwp.value, gwp.unit) - if val < q("1 kgCO2eq"): - val = val.to("gCO2eq") - return val - - -def format_adpe(adpe: ADPe) -> Quantity: - return q(adpe.value, adpe.unit) - - -def format_pe(pe: PE) -> Quantity: - val = q(pe.value, pe.unit) - if val < q("1 MJ"): - val = val.to("kJ") - return val - -def format_impacts(impacts: Impacts) -> QImpacts: - try: - impacts.energy.value = (impacts.energy.value.max + impacts.energy.value.min)/2 - impacts.gwp.value = (impacts.gwp.value.max + impacts.gwp.value.min)/2 - impacts.adpe.value = (impacts.adpe.value.max + impacts.adpe.value.min)/2 - impacts.pe.value = (impacts.pe.value.max + impacts.pe.value.min)/2 - return QImpacts( - energy=format_energy(impacts.energy), - gwp=format_gwp(impacts.gwp), - adpe=format_adpe(impacts.adpe), - pe=format_pe(impacts.pe) - ) - except: #when no range - return QImpacts( - energy=format_energy(impacts.energy), - gwp=format_gwp(impacts.gwp), - adpe=format_adpe(impacts.adpe), - pe=format_pe(impacts.pe) - ) - -def format_impacts_expert(impacts: Impacts) -> QImpacts: - return QImpacts( - energy=format_energy(impacts.energy), - gwp=format_gwp(impacts.gwp), - adpe=format_adpe(impacts.adpe), - pe=format_pe(impacts.pe) - - ), impacts.usage, impacts.embodied - - -def format_energy_eq_physical_activity(energy: Quantity) -> tuple[PhysicalActivity, Quantity]: - energy = energy.to("kJ") - running_eq = energy / RUNNING_ENERGY_EQ - if running_eq > q("1 km"): - return PhysicalActivity.RUNNING, running_eq - - walking_eq = energy / WALKING_ENERGY_EQ - if walking_eq < q("1 km"): - walking_eq = walking_eq.to("meter") - return PhysicalActivity.WALKING, walking_eq - - -def format_energy_eq_electric_vehicle(energy: Quantity) -> Quantity: - energy = energy.to("kWh") - ev_eq = energy / EV_ENERGY_EQ - if ev_eq < q("1 km"): - ev_eq = ev_eq.to("meter") - return ev_eq - - -def format_gwp_eq_streaming(gwp: Quantity) -> Quantity: - gwp = gwp.to("kgCO2eq") - streaming_eq = gwp * STREAMING_GWP_EQ - if streaming_eq < q("1 h"): - streaming_eq = streaming_eq.to("min") - if streaming_eq < q("1 min"): - streaming_eq = streaming_eq.to("s") - return streaming_eq - - -def format_energy_eq_electricity_production(energy: Quantity) -> tuple[EnergyProduction, Quantity]: - electricity_eq = energy * ONE_PERCENT_WORLD_POPULATION * DAYS_IN_YEAR - electricity_eq = electricity_eq.to("TWh") - if electricity_eq > YEARLY_NUCLEAR_ENERGY_EQ: - return EnergyProduction.NUCLEAR, electricity_eq / YEARLY_NUCLEAR_ENERGY_EQ - electricity_eq = electricity_eq.to("GWh") - return EnergyProduction.WIND, electricity_eq / YEARLY_WIND_ENERGY_EQ - - -def format_energy_eq_electricity_consumption_ireland(energy: Quantity) -> Quantity: - electricity_eq = energy * ONE_PERCENT_WORLD_POPULATION * DAYS_IN_YEAR - electricity_eq = electricity_eq.to("TWh") - return electricity_eq / YEARLY_IRELAND_ELECTRICITY_CONSUMPTION - - -def format_gwp_eq_airplane_paris_nyc(gwp: Quantity) -> Quantity: - gwp_eq = gwp * ONE_PERCENT_WORLD_POPULATION * DAYS_IN_YEAR - gwp_eq = gwp_eq.to("kgCO2eq") - return gwp_eq / AIRPLANE_PARIS_NYC_GWP_EQ +from dataclasses import dataclass +from enum import Enum + +import pandas as pd +from ecologits.model_repository import models +from ecologits.impacts.modeling import Impacts, Energy, GWP, ADPe, PE +#from ecologits.tracers.utils import llm_impacts +from pint import UnitRegistry, Quantity + +##################################################################################### +### UNITS DEFINITION +##################################################################################### + +u = UnitRegistry() +u.define('Wh = watt_hour') +u.define('kWh = kilowatt_hour') +u.define('MWh = megawatt_hour') +u.define('GWh = gigawatt_hour') +u.define('TWh = terawatt_hour') +u.define('gCO2eq = gram') +u.define('kgCO2eq = kilogram') +u.define('tCO2eq = metricton') +u.define('kgSbeq = kilogram') +u.define('kJ = kilojoule') +u.define('MJ = megajoule') +u.define('m = meter') +u.define('km = kilometer') +u.define('s = second') +u.define('min = minute') +u.define('h = hour') +q = u.Quantity + +@dataclass +class QImpacts: + energy: Quantity + gwp: Quantity + adpe: Quantity + pe: Quantity + + +class PhysicalActivity(str, Enum): + RUNNING = "running" + WALKING = "walking" + + +class EnergyProduction(str, Enum): + NUCLEAR = "nuclear" + WIND = "wind" + + +COUNTRIES = [ + ("cook_islands", 38.81, 9_556), + ("tonga", 51.15, 104_490), + ("comoros", 100, 821_632), + ("samoa", 100, 821_632), +] + +##################################################################################### +### EQUIVALENT RAW DATA +##################################################################################### + +# From https://www.runningtools.com/energyusage.htm +RUNNING_ENERGY_EQ = q("294 kJ / km") # running 1 km at 10 km/h with a weight of 70 kg +WALKING_ENERGY_EQ = q("196 kJ / km") # walking 1 km at 3 km/h with a weight of 70 kg + +# From https://selectra.info/energie/actualites/insolite/consommation-vehicules-electriques-france-2040 +# and https://www.tesla.com/fr_fr/support/power-consumption +EV_ENERGY_EQ = q("0.17 kWh / km") + +# From https://impactco2.fr/outils/comparateur?value=1&comparisons=streamingvideo +STREAMING_GWP_EQ = q("15.6 h / kgCO2eq") + +# From https://ourworldindata.org/population-growth +ONE_PERCENT_WORLD_POPULATION = 80_000_000 + +DAYS_IN_YEAR = 365 + +# For a 900 MW nuclear plant -> 500 000 MWh / month +# From https://www.edf.fr/groupe-edf/espaces-dedies/jeunes-enseignants/pour-les-jeunes/lenergie-de-a-a-z/produire-de-lelectricite/le-nucleaire-en-chiffres +YEARLY_NUCLEAR_ENERGY_EQ = q("6 TWh") + +# For a 2MW wind turbine +# https://www.ecologie.gouv.fr/eolien-terrestre +YEARLY_WIND_ENERGY_EQ = q("4.2 GWh") + +# Ireland yearly electricity consumption +# From https://en.wikipedia.org/wiki/List_of_countries_by_electricity_consumption +YEARLY_IRELAND_ELECTRICITY_CONSUMPTION = q("33 TWh") +IRELAND_POPULATION_MILLION = 5 + +# From https://impactco2.fr/outils/comparateur?value=1&comparisons=&equivalent=avion-pny +AIRPLANE_PARIS_NYC_GWP_EQ = q("177000 kgCO2eq") + +def filter_models(provider, list_models): + + model = 1 + + return model + +##################################################################################### +### IMPACTS FORMATING +##################################################################################### + +def format_energy(energy: Energy) -> Quantity: + val = q(energy.value, energy.unit) + if val < q("1 kWh"): + val = val.to("Wh") + return val + +def format_gwp(gwp: GWP) -> Quantity: + val = q(gwp.value, gwp.unit) + if val < q("1 kgCO2eq"): + val = val.to("gCO2eq") + return val + +def format_adpe(adpe: ADPe) -> Quantity: + return q(adpe.value, adpe.unit) + +def format_pe(pe: PE) -> Quantity: + val = q(pe.value, pe.unit) + if val < q("1 MJ"): + val = val.to("kJ") + return val + +def format_impacts(impacts: Impacts) -> QImpacts: + + try: + impacts.energy.value = (impacts.energy.value.max + impacts.energy.value.min)/2 + impacts.gwp.value = (impacts.gwp.value.max + impacts.gwp.value.min)/2 + impacts.adpe.value = (impacts.adpe.value.max + impacts.adpe.value.min)/2 + impacts.pe.value = (impacts.pe.value.max + impacts.pe.value.min)/2 + return QImpacts( + energy=format_energy(impacts.energy), + gwp=format_gwp(impacts.gwp), + adpe=format_adpe(impacts.adpe), + pe=format_pe(impacts.pe) + ), impacts.usage, impacts.embodied + except: #when no range + return QImpacts( + energy=format_energy(impacts.energy), + gwp=format_gwp(impacts.gwp), + adpe=format_adpe(impacts.adpe), + pe=format_pe(impacts.pe) + ), impacts.usage, impacts.embodied + +def split_impacts_u_e(impacts: Impacts) -> QImpacts: + return impacts.usage, impacts.embodied + +def average_range_impacts(RangeValue): + return (RangeValue.max + RangeValue.min)/2 + +def format_impacts_expert(impacts: Impacts, display_range: bool) -> QImpacts: + + if display_range: + return QImpacts( + energy=format_energy(impacts.energy), + gwp=format_gwp(impacts.gwp), + adpe=format_adpe(impacts.adpe), + pe=format_pe(impacts.pe) + ), impacts.usage, impacts.embodied + + else: + energy = {"value":(impacts.energy.value.max + impacts.energy.value.min)/2, "unit":impacts.energy.unit} + gwp = (impacts.gwp.value.max + impacts.gwp.value.min)/2 + adpe = (impacts.adpe.value.max + impacts.adpe.value.min)/2 + pe = (impacts.pe.value.max + impacts.pe.value.min)/2 + return QImpacts( + energy=format_energy(energy), + gwp=format_gwp(gwp), + adpe=format_adpe(adpe), + pe=format_pe(pe) + ), impacts.usage, impacts.embodied + +##################################################################################### +### EQUIVALENT FORMATING +##################################################################################### + +def format_energy_eq_physical_activity(energy: Quantity) -> tuple[PhysicalActivity, Quantity]: + energy = energy.to("kJ") + running_eq = energy / RUNNING_ENERGY_EQ + if running_eq > q("1 km"): + return PhysicalActivity.RUNNING, running_eq + + walking_eq = energy / WALKING_ENERGY_EQ + if walking_eq < q("1 km"): + walking_eq = walking_eq.to("meter") + return PhysicalActivity.WALKING, walking_eq + +def format_energy_eq_electric_vehicle(energy: Quantity) -> Quantity: + energy = energy.to("kWh") + ev_eq = energy / EV_ENERGY_EQ + if ev_eq < q("1 km"): + ev_eq = ev_eq.to("meter") + return ev_eq + +def format_gwp_eq_streaming(gwp: Quantity) -> Quantity: + gwp = gwp.to("kgCO2eq") + streaming_eq = gwp * STREAMING_GWP_EQ + if streaming_eq < q("1 h"): + streaming_eq = streaming_eq.to("min") + if streaming_eq < q("1 min"): + streaming_eq = streaming_eq.to("s") + return streaming_eq + +def format_energy_eq_electricity_production(energy: Quantity) -> tuple[EnergyProduction, Quantity]: + electricity_eq = energy * ONE_PERCENT_WORLD_POPULATION * DAYS_IN_YEAR + electricity_eq = electricity_eq.to("TWh") + if electricity_eq > YEARLY_NUCLEAR_ENERGY_EQ: + return EnergyProduction.NUCLEAR, electricity_eq / YEARLY_NUCLEAR_ENERGY_EQ + electricity_eq = electricity_eq.to("GWh") + return EnergyProduction.WIND, electricity_eq / YEARLY_WIND_ENERGY_EQ + + +def format_energy_eq_electricity_consumption_ireland(energy: Quantity) -> Quantity: + electricity_eq = energy * ONE_PERCENT_WORLD_POPULATION * DAYS_IN_YEAR + electricity_eq = electricity_eq.to("TWh") + return electricity_eq / YEARLY_IRELAND_ELECTRICITY_CONSUMPTION + +def format_gwp_eq_airplane_paris_nyc(gwp: Quantity) -> Quantity: + gwp_eq = gwp * ONE_PERCENT_WORLD_POPULATION * DAYS_IN_YEAR + gwp_eq = gwp_eq.to("kgCO2eq") + return gwp_eq / AIRPLANE_PARIS_NYC_GWP_EQ + +##################################################################################### +### MODELS PARAMETERS +##################################################################################### + +def model_active_params_fn(provider_name: str, model_name: str, n_param: float): + if model_name == 'CUSTOM': + return n_param + else: + model = models.find_model(provider=provider_name, model_name=model_name) + + if model.architecture == 'moe': + try: + return model.architecture.parameters.active.max + except: + try: + return model.architecture.parameters.active + except: + return model.architecture.parameters + elif model.architecture == 'dense': + try: #dense with range + return model.architecture.parameters.max + except: #dense without range + return model.architecture.parameters + +def model_total_params_fn(provider_name: str, model_name: str, n_param: float): + if model_name == 'CUSTOM': + return n_param + provider, model_name = model_name.split('/', 1) + model = models.find_model(provider=provider, model_name=model_name) + try: #moe + return model.architecture.parameters.total.max + except: + try: #dense with range + return model.architecture.parameters.max + except: #dense without range + try: + return model.architecture.parameters.total + except: + return model.architecture.parameters \ No newline at end of file diff --git a/utils.py b/utils.py new file mode 100755 index 0000000000000000000000000000000000000000..af4187d59db6f21451bd8b4a921a5fee808f99e6 --- /dev/null +++ b/utils.py @@ -0,0 +1,262 @@ +from dataclasses import dataclass +from enum import Enum + +import pandas as pd +from ecologits.model_repository import models +from ecologits.impacts.modeling import Impacts, Energy, GWP, ADPe, PE +#from ecologits.tracers.utils import llm_impacts +from pint import UnitRegistry, Quantity + +##################################################################################### +### UNITS DEFINITION +##################################################################################### + +u = UnitRegistry() +u.define('Wh = watt_hour') +u.define('kWh = kilowatt_hour') +u.define('MWh = megawatt_hour') +u.define('GWh = gigawatt_hour') +u.define('TWh = terawatt_hour') +u.define('gCO2eq = gram') +u.define('kgCO2eq = kilogram') +u.define('tCO2eq = metricton') +u.define('kgSbeq = kilogram') +u.define('kJ = kilojoule') +u.define('MJ = megajoule') +u.define('m = meter') +u.define('km = kilometer') +u.define('s = second') +u.define('min = minute') +u.define('h = hour') +q = u.Quantity + +@dataclass +class QImpacts: + energy: Quantity + gwp: Quantity + adpe: Quantity + pe: Quantity + + +class PhysicalActivity(str, Enum): + RUNNING = "running" + WALKING = "walking" + + +class EnergyProduction(str, Enum): + NUCLEAR = "nuclear" + WIND = "wind" + + +COUNTRIES = [ + ("cook_islands", 38.81, 9_556), + ("tonga", 51.15, 104_490), + ("comoros", 100, 821_632), + ("samoa", 100, 821_632), +] + +##################################################################################### +### EQUIVALENT RAW DATA +##################################################################################### + +# From https://www.runningtools.com/energyusage.htm +RUNNING_ENERGY_EQ = q("294 kJ / km") # running 1 km at 10 km/h with a weight of 70 kg +WALKING_ENERGY_EQ = q("196 kJ / km") # walking 1 km at 3 km/h with a weight of 70 kg + +# From https://selectra.info/energie/actualites/insolite/consommation-vehicules-electriques-france-2040 +# and https://www.tesla.com/fr_fr/support/power-consumption +EV_ENERGY_EQ = q("0.17 kWh / km") + +# From https://impactco2.fr/outils/comparateur?value=1&comparisons=streamingvideo +STREAMING_GWP_EQ = q("15.6 h / kgCO2eq") + +# From https://ourworldindata.org/population-growth +ONE_PERCENT_WORLD_POPULATION = 80_000_000 + +DAYS_IN_YEAR = 365 + +# For a 900 MW nuclear plant -> 500 000 MWh / month +# From https://www.edf.fr/groupe-edf/espaces-dedies/jeunes-enseignants/pour-les-jeunes/lenergie-de-a-a-z/produire-de-lelectricite/le-nucleaire-en-chiffres +YEARLY_NUCLEAR_ENERGY_EQ = q("6 TWh") + +# For a 2MW wind turbine +# https://www.ecologie.gouv.fr/eolien-terrestre +YEARLY_WIND_ENERGY_EQ = q("4.2 GWh") + +# Ireland yearly electricity consumption +# From https://en.wikipedia.org/wiki/List_of_countries_by_electricity_consumption +YEARLY_IRELAND_ELECTRICITY_CONSUMPTION = q("33 TWh") +IRELAND_POPULATION_MILLION = 5 + +# From https://impactco2.fr/outils/comparateur?value=1&comparisons=&equivalent=avion-pny +AIRPLANE_PARIS_NYC_GWP_EQ = q("177000 kgCO2eq") + +def filter_models(provider, list_models): + + model = 1 + + return model + +##################################################################################### +### IMPACTS FORMATING +##################################################################################### + +def format_energy(energy: Energy) -> Quantity: + val = q(energy.value, energy.unit) + if val < q("1 kWh"): + val = val.to("Wh") + return val + +def format_gwp(gwp: GWP) -> Quantity: + val = q(gwp.value, gwp.unit) + if val < q("1 kgCO2eq"): + val = val.to("gCO2eq") + return val + +def format_adpe(adpe: ADPe) -> Quantity: + return q(adpe.value, adpe.unit) + +def format_pe(pe: PE) -> Quantity: + val = q(pe.value, pe.unit) + if val < q("1 MJ"): + val = val.to("kJ") + return val + +def format_impacts(impacts: Impacts) -> QImpacts: + + try: + impacts.energy.value = (impacts.energy.value.max + impacts.energy.value.min)/2 + impacts.gwp.value = (impacts.gwp.value.max + impacts.gwp.value.min)/2 + impacts.adpe.value = (impacts.adpe.value.max + impacts.adpe.value.min)/2 + impacts.pe.value = (impacts.pe.value.max + impacts.pe.value.min)/2 + return QImpacts( + energy=format_energy(impacts.energy), + gwp=format_gwp(impacts.gwp), + adpe=format_adpe(impacts.adpe), + pe=format_pe(impacts.pe) + ), impacts.usage, impacts.embodied + except: #when no range + return QImpacts( + energy=format_energy(impacts.energy), + gwp=format_gwp(impacts.gwp), + adpe=format_adpe(impacts.adpe), + pe=format_pe(impacts.pe) + ), impacts.usage, impacts.embodied + +def split_impacts_u_e(impacts: Impacts) -> QImpacts: + return impacts.usage, impacts.embodied + +def average_range_impacts(RangeValue): + return (RangeValue.max + RangeValue.min)/2 + +def format_impacts_expert(impacts: Impacts, display_range: bool) -> QImpacts: + + if display_range: + return QImpacts( + energy=format_energy(impacts.energy), + gwp=format_gwp(impacts.gwp), + adpe=format_adpe(impacts.adpe), + pe=format_pe(impacts.pe) + ), impacts.usage, impacts.embodied + + else: + energy = {"value":(impacts.energy.value.max + impacts.energy.value.min)/2, "unit":impacts.energy.unit} + gwp = (impacts.gwp.value.max + impacts.gwp.value.min)/2 + adpe = (impacts.adpe.value.max + impacts.adpe.value.min)/2 + pe = (impacts.pe.value.max + impacts.pe.value.min)/2 + return QImpacts( + energy=format_energy(energy), + gwp=format_gwp(gwp), + adpe=format_adpe(adpe), + pe=format_pe(pe) + ), impacts.usage, impacts.embodied + +##################################################################################### +### EQUIVALENT FORMATING +##################################################################################### + +def format_energy_eq_physical_activity(energy: Quantity) -> tuple[PhysicalActivity, Quantity]: + energy = energy.to("kJ") + running_eq = energy / RUNNING_ENERGY_EQ + if running_eq > q("1 km"): + return PhysicalActivity.RUNNING, running_eq + + walking_eq = energy / WALKING_ENERGY_EQ + if walking_eq < q("1 km"): + walking_eq = walking_eq.to("meter") + return PhysicalActivity.WALKING, walking_eq + +def format_energy_eq_electric_vehicle(energy: Quantity) -> Quantity: + energy = energy.to("kWh") + ev_eq = energy / EV_ENERGY_EQ + if ev_eq < q("1 km"): + ev_eq = ev_eq.to("meter") + return ev_eq + +def format_gwp_eq_streaming(gwp: Quantity) -> Quantity: + gwp = gwp.to("kgCO2eq") + streaming_eq = gwp * STREAMING_GWP_EQ + if streaming_eq < q("1 h"): + streaming_eq = streaming_eq.to("min") + if streaming_eq < q("1 min"): + streaming_eq = streaming_eq.to("s") + return streaming_eq + +def format_energy_eq_electricity_production(energy: Quantity) -> tuple[EnergyProduction, Quantity]: + electricity_eq = energy * ONE_PERCENT_WORLD_POPULATION * DAYS_IN_YEAR + electricity_eq = electricity_eq.to("TWh") + if electricity_eq > YEARLY_NUCLEAR_ENERGY_EQ: + return EnergyProduction.NUCLEAR, electricity_eq / YEARLY_NUCLEAR_ENERGY_EQ + electricity_eq = electricity_eq.to("GWh") + return EnergyProduction.WIND, electricity_eq / YEARLY_WIND_ENERGY_EQ + + +def format_energy_eq_electricity_consumption_ireland(energy: Quantity) -> Quantity: + electricity_eq = energy * ONE_PERCENT_WORLD_POPULATION * DAYS_IN_YEAR + electricity_eq = electricity_eq.to("TWh") + return electricity_eq / YEARLY_IRELAND_ELECTRICITY_CONSUMPTION + +def format_gwp_eq_airplane_paris_nyc(gwp: Quantity) -> Quantity: + gwp_eq = gwp * ONE_PERCENT_WORLD_POPULATION * DAYS_IN_YEAR + gwp_eq = gwp_eq.to("kgCO2eq") + return gwp_eq / AIRPLANE_PARIS_NYC_GWP_EQ + +##################################################################################### +### MODELS PARAMETERS +##################################################################################### + +def model_active_params_fn(provider_name: str, model_name: str, n_param: float): + if model_name == 'CUSTOM': + return n_param + else: + model = models.find_model(provider=provider_name, model_name=model_name) + + if model.architecture == 'moe': + try: + return model.architecture.parameters.active.max + except: + try: + return model.architecture.parameters.active + except: + return model.architecture.parameters + elif model.architecture == 'dense': + try: #dense with range + return model.architecture.parameters.max + except: #dense without range + return model.architecture.parameters + +def model_total_params_fn(provider_name: str, model_name: str, n_param: float): + if model_name == 'CUSTOM': + return n_param + provider, model_name = model_name.split('/', 1) + model = models.find_model(provider=provider, model_name=model_name) + try: #moe + return model.architecture.parameters.total.max + except: + try: #dense with range + return model.architecture.parameters.max + except: #dense without range + try: + return model.architecture.parameters.total + except: + return model.architecture.parameters \ No newline at end of file diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000000000000000000000000000000000000..88114dbfba0561b2ffb1e79952fa4501f1fefdd4 --- /dev/null +++ b/uv.lock @@ -0,0 +1,902 @@ +version = 1 +revision = 1 +requires-python = ">=3.12" + +[[package]] +name = "altair" +version = "5.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jinja2" }, + { name = "jsonschema" }, + { name = "narwhals" }, + { name = "packaging" }, + { name = "typing-extensions", marker = "python_full_version < '3.14'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/16/b1/f2969c7bdb8ad8bbdda031687defdce2c19afba2aa2c8e1d2a17f78376d8/altair-5.5.0.tar.gz", hash = "sha256:d960ebe6178c56de3855a68c47b516be38640b73fb3b5111c2a9ca90546dd73d", size = 705305 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/f3/0b6ced594e51cc95d8c1fc1640d3623770d01e4969d29c0bd09945fafefa/altair-5.5.0-py3-none-any.whl", hash = "sha256:91a310b926508d560fe0148d02a194f38b824122641ef528113d029fcd129f8c", size = 731200 }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 }, +] + +[[package]] +name = "anyio" +version = "4.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "sniffio" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/73/199a98fc2dae33535d6b8e8e6ec01f8c1d76c9adb096c6b7d64823038cde/anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a", size = 181126 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/46/eb/e7f063ad1fec6b3178a3cd82d1a3c4de82cccf283fc42746168188e1cdd5/anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a", size = 96041 }, +] + +[[package]] +name = "attrs" +version = "25.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/49/7c/fdf464bcc51d23881d110abd74b512a42b3d5d376a55a831b44c603ae17f/attrs-25.1.0.tar.gz", hash = "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e", size = 810562 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/30/d4986a882011f9df997a55e6becd864812ccfcd821d64aac8570ee39f719/attrs-25.1.0-py3-none-any.whl", hash = "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a", size = 63152 }, +] + +[[package]] +name = "blinker" +version = "1.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", hash = "sha256:b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf", size = 22460 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", hash = "sha256:ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc", size = 8458 }, +] + +[[package]] +name = "cachetools" +version = "5.5.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6c/81/3747dad6b14fa2cf53fcf10548cf5aea6913e96fab41a3c198676f8948a5/cachetools-5.5.2.tar.gz", hash = "sha256:1a661caa9175d26759571b2e19580f9d6393969e5dfca11fdb1f947a23e640d4", size = 28380 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/72/76/20fa66124dbe6be5cafeb312ece67de6b61dd91a0247d1ea13db4ebb33c2/cachetools-5.5.2-py3-none-any.whl", hash = "sha256:d26a22bcc62eb95c3beabd9f1ee5e820d3d2704fe2967cbe350e20c8ffcd3f0a", size = 10080 }, +] + +[[package]] +name = "certifi" +version = "2025.1.31" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/16/b0/572805e227f01586461c80e0fd25d65a2115599cc9dad142fee4b747c357/charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", size = 123188 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0a/9a/dd1e1cdceb841925b7798369a09279bd1cf183cef0f9ddf15a3a6502ee45/charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", size = 196105 }, + { url = "https://files.pythonhosted.org/packages/d3/8c/90bfabf8c4809ecb648f39794cf2a84ff2e7d2a6cf159fe68d9a26160467/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", size = 140404 }, + { url = "https://files.pythonhosted.org/packages/ad/8f/e410d57c721945ea3b4f1a04b74f70ce8fa800d393d72899f0a40526401f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", size = 150423 }, + { url = "https://files.pythonhosted.org/packages/f0/b8/e6825e25deb691ff98cf5c9072ee0605dc2acfca98af70c2d1b1bc75190d/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", size = 143184 }, + { url = "https://files.pythonhosted.org/packages/3e/a2/513f6cbe752421f16d969e32f3583762bfd583848b763913ddab8d9bfd4f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", size = 145268 }, + { url = "https://files.pythonhosted.org/packages/74/94/8a5277664f27c3c438546f3eb53b33f5b19568eb7424736bdc440a88a31f/charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616", size = 147601 }, + { url = "https://files.pythonhosted.org/packages/7c/5f/6d352c51ee763623a98e31194823518e09bfa48be2a7e8383cf691bbb3d0/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", size = 141098 }, + { url = "https://files.pythonhosted.org/packages/78/d4/f5704cb629ba5ab16d1d3d741396aec6dc3ca2b67757c45b0599bb010478/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", size = 149520 }, + { url = "https://files.pythonhosted.org/packages/c5/96/64120b1d02b81785f222b976c0fb79a35875457fa9bb40827678e54d1bc8/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", size = 152852 }, + { url = "https://files.pythonhosted.org/packages/84/c9/98e3732278a99f47d487fd3468bc60b882920cef29d1fa6ca460a1fdf4e6/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", size = 150488 }, + { url = "https://files.pythonhosted.org/packages/13/0e/9c8d4cb99c98c1007cc11eda969ebfe837bbbd0acdb4736d228ccaabcd22/charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", size = 146192 }, + { url = "https://files.pythonhosted.org/packages/b2/21/2b6b5b860781a0b49427309cb8670785aa543fb2178de875b87b9cc97746/charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", size = 95550 }, + { url = "https://files.pythonhosted.org/packages/21/5b/1b390b03b1d16c7e382b561c5329f83cc06623916aab983e8ab9239c7d5c/charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", size = 102785 }, + { url = "https://files.pythonhosted.org/packages/38/94/ce8e6f63d18049672c76d07d119304e1e2d7c6098f0841b51c666e9f44a0/charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", size = 195698 }, + { url = "https://files.pythonhosted.org/packages/24/2e/dfdd9770664aae179a96561cc6952ff08f9a8cd09a908f259a9dfa063568/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", size = 140162 }, + { url = "https://files.pythonhosted.org/packages/24/4e/f646b9093cff8fc86f2d60af2de4dc17c759de9d554f130b140ea4738ca6/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", size = 150263 }, + { url = "https://files.pythonhosted.org/packages/5e/67/2937f8d548c3ef6e2f9aab0f6e21001056f692d43282b165e7c56023e6dd/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", size = 142966 }, + { url = "https://files.pythonhosted.org/packages/52/ed/b7f4f07de100bdb95c1756d3a4d17b90c1a3c53715c1a476f8738058e0fa/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", size = 144992 }, + { url = "https://files.pythonhosted.org/packages/96/2c/d49710a6dbcd3776265f4c923bb73ebe83933dfbaa841c5da850fe0fd20b/charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", size = 147162 }, + { url = "https://files.pythonhosted.org/packages/b4/41/35ff1f9a6bd380303dea55e44c4933b4cc3c4850988927d4082ada230273/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", size = 140972 }, + { url = "https://files.pythonhosted.org/packages/fb/43/c6a0b685fe6910d08ba971f62cd9c3e862a85770395ba5d9cad4fede33ab/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", size = 149095 }, + { url = "https://files.pythonhosted.org/packages/4c/ff/a9a504662452e2d2878512115638966e75633519ec11f25fca3d2049a94a/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", size = 152668 }, + { url = "https://files.pythonhosted.org/packages/6c/71/189996b6d9a4b932564701628af5cee6716733e9165af1d5e1b285c530ed/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", size = 150073 }, + { url = "https://files.pythonhosted.org/packages/e4/93/946a86ce20790e11312c87c75ba68d5f6ad2208cfb52b2d6a2c32840d922/charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", size = 145732 }, + { url = "https://files.pythonhosted.org/packages/cd/e5/131d2fb1b0dddafc37be4f3a2fa79aa4c037368be9423061dccadfd90091/charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", size = 95391 }, + { url = "https://files.pythonhosted.org/packages/27/f2/4f9a69cc7712b9b5ad8fdb87039fd89abba997ad5cbe690d1835d40405b0/charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", size = 102702 }, + { url = "https://files.pythonhosted.org/packages/0e/f6/65ecc6878a89bb1c23a086ea335ad4bf21a588990c3f535a227b9eea9108/charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", size = 49767 }, +] + +[[package]] +name = "click" +version = "8.1.8" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, +] + +[[package]] +name = "ecologits" +version = "0.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "httpx" }, + { name = "packaging" }, + { name = "pydantic" }, + { name = "requests" }, + { name = "tqdm" }, + { name = "wrapt" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f4/5f/1ac13460402ee44fa0f6bf3ff32768ef6758c8535bf640dd696bcd512ca8/ecologits-0.6.1.tar.gz", hash = "sha256:e87fce1787ce05994cc732a56c870a705c1d1cbf7a669b850c17a52b4af94f75", size = 31511 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/87/3f039436a65beefd3888979fb366ab690b53d427ccd040538d8c4fd4a8fe/ecologits-0.6.1-py3-none-any.whl", hash = "sha256:0d8721e97864739f31ef329189a0183dc7f386a98efc480b15e3b1a390e41746", size = 39511 }, +] + +[[package]] +name = "ecologits-calculator-streamlit" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "ecologits" }, + { name = "pint" }, + { name = "plotly" }, + { name = "streamlit" }, +] + +[package.metadata] +requires-dist = [ + { name = "ecologits", specifier = ">=0.6.1" }, + { name = "pint", specifier = ">=0.24.4" }, + { name = "plotly", specifier = ">=6.0.0" }, + { name = "streamlit", specifier = ">=1.43.1" }, +] + +[[package]] +name = "flexcache" +version = "0.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/b0/8a21e330561c65653d010ef112bf38f60890051d244ede197ddaa08e50c1/flexcache-0.3.tar.gz", hash = "sha256:18743bd5a0621bfe2cf8d519e4c3bfdf57a269c15d1ced3fb4b64e0ff4600656", size = 15816 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/27/cd/c883e1a7c447479d6e13985565080e3fea88ab5a107c21684c813dba1875/flexcache-0.3-py3-none-any.whl", hash = "sha256:d43c9fea82336af6e0115e308d9d33a185390b8346a017564611f1466dcd2e32", size = 13263 }, +] + +[[package]] +name = "flexparser" +version = "0.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/82/99/b4de7e39e8eaf8207ba1a8fa2241dd98b2ba72ae6e16960d8351736d8702/flexparser-0.4.tar.gz", hash = "sha256:266d98905595be2ccc5da964fe0a2c3526fbbffdc45b65b3146d75db992ef6b2", size = 31799 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/5e/3be305568fe5f34448807976dc82fc151d76c3e0e03958f34770286278c1/flexparser-0.4-py3-none-any.whl", hash = "sha256:3738b456192dcb3e15620f324c447721023c0293f6af9955b481e91d00179846", size = 27625 }, +] + +[[package]] +name = "gitdb" +version = "4.0.12" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "smmap" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794 }, +] + +[[package]] +name = "gitpython" +version = "3.1.44" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "gitdb" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c0/89/37df0b71473153574a5cdef8f242de422a0f5d26d7a9e231e6f169b4ad14/gitpython-3.1.44.tar.gz", hash = "sha256:c87e30b26253bf5418b01b0660f818967f3c503193838337fe5e573331249269", size = 214196 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/9a/4114a9057db2f1462d5c8f8390ab7383925fe1ac012eaa42402ad65c2963/GitPython-3.1.44-py3-none-any.whl", hash = "sha256:9e0e10cda9bed1ee64bc9a6de50e7e38a9c9943241cd7f585f6df3ed28011110", size = 207599 }, +] + +[[package]] +name = "h11" +version = "0.14.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 }, +] + +[[package]] +name = "httpcore" +version = "1.0.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "h11" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6a/41/d7d0a89eb493922c37d343b607bc1b5da7f5be7e383740b4753ad8943e90/httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", size = 85196 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/f5/72347bc88306acb359581ac4d52f23c0ef445b57157adedb9aee0cd689d2/httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd", size = 78551 }, +] + +[[package]] +name = "httpx" +version = "0.28.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "certifi" }, + { name = "httpcore" }, + { name = "idna" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 }, +] + +[[package]] +name = "idna" +version = "3.10" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 }, +] + +[[package]] +name = "jinja2" +version = "3.1.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markupsafe" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899 }, +] + +[[package]] +name = "jsonschema" +version = "4.23.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "jsonschema-specifications" }, + { name = "referencing" }, + { name = "rpds-py" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/2e/03362ee4034a4c917f697890ccd4aec0800ccf9ded7f511971c75451deec/jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4", size = 325778 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/69/4a/4f9dbeb84e8850557c02365a0eee0649abe5eb1d84af92a25731c6c0f922/jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566", size = 88462 }, +] + +[[package]] +name = "jsonschema-specifications" +version = "2024.10.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "referencing" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/10/db/58f950c996c793472e336ff3655b13fbcf1e3b359dcf52dcf3ed3b52c352/jsonschema_specifications-2024.10.1.tar.gz", hash = "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272", size = 15561 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/0f/8910b19ac0670a0f80ce1008e5e751c4a57e14d2c4c13a482aa6079fa9d6/jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf", size = 18459 }, +] + +[[package]] +name = "markupsafe" +version = "3.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 }, + { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 }, + { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 }, + { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 }, + { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 }, + { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 }, + { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 }, + { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 }, + { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 }, + { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, + { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, + { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, + { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, + { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, + { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, + { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, + { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, + { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, + { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, + { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, + { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, + { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, + { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, + { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, + { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, + { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, + { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, + { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, + { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, +] + +[[package]] +name = "narwhals" +version = "1.29.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a7/17/7d35094da0820ae941d8ce51842f253da36c6f95360ea0afabfc18bc02c6/narwhals-1.29.1.tar.gz", hash = "sha256:c408acf09e90c116f247cf34f24a3a89d147e3e235b1d3c708cfd1960baf320a", size = 251464 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/22/380df533b08a57bc9013bb5714f33c571e1447828d83213a66adaefc0a04/narwhals-1.29.1-py3-none-any.whl", hash = "sha256:2f68cfbb2562672c4dfa54f158ed8c2828e9920ef784981cd9114e419c444216", size = 308220 }, +] + +[[package]] +name = "numpy" +version = "2.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fb/90/8956572f5c4ae52201fdec7ba2044b2c882832dcec7d5d0922c9e9acf2de/numpy-2.2.3.tar.gz", hash = "sha256:dbdc15f0c81611925f382dfa97b3bd0bc2c1ce19d4fe50482cb0ddc12ba30020", size = 20262700 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/ec/43628dcf98466e087812142eec6d1c1a6c6bdfdad30a0aa07b872dc01f6f/numpy-2.2.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12c045f43b1d2915eca6b880a7f4a256f59d62df4f044788c8ba67709412128d", size = 20929458 }, + { url = "https://files.pythonhosted.org/packages/9b/c0/2f4225073e99a5c12350954949ed19b5d4a738f541d33e6f7439e33e98e4/numpy-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:87eed225fd415bbae787f93a457af7f5990b92a334e346f72070bf569b9c9c95", size = 14115299 }, + { url = "https://files.pythonhosted.org/packages/ca/fa/d2c5575d9c734a7376cc1592fae50257ec95d061b27ee3dbdb0b3b551eb2/numpy-2.2.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:712a64103d97c404e87d4d7c47fb0c7ff9acccc625ca2002848e0d53288b90ea", size = 5145723 }, + { url = "https://files.pythonhosted.org/packages/eb/dc/023dad5b268a7895e58e791f28dc1c60eb7b6c06fcbc2af8538ad069d5f3/numpy-2.2.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:a5ae282abe60a2db0fd407072aff4599c279bcd6e9a2475500fc35b00a57c532", size = 6678797 }, + { url = "https://files.pythonhosted.org/packages/3f/19/bcd641ccf19ac25abb6fb1dcd7744840c11f9d62519d7057b6ab2096eb60/numpy-2.2.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5266de33d4c3420973cf9ae3b98b54a2a6d53a559310e3236c4b2b06b9c07d4e", size = 14067362 }, + { url = "https://files.pythonhosted.org/packages/39/04/78d2e7402fb479d893953fb78fa7045f7deb635ec095b6b4f0260223091a/numpy-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b787adbf04b0db1967798dba8da1af07e387908ed1553a0d6e74c084d1ceafe", size = 16116679 }, + { url = "https://files.pythonhosted.org/packages/d0/a1/e90f7aa66512be3150cb9d27f3d9995db330ad1b2046474a13b7040dfd92/numpy-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:34c1b7e83f94f3b564b35f480f5652a47007dd91f7c839f404d03279cc8dd021", size = 15264272 }, + { url = "https://files.pythonhosted.org/packages/dc/b6/50bd027cca494de4fa1fc7bf1662983d0ba5f256fa0ece2c376b5eb9b3f0/numpy-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4d8335b5f1b6e2bce120d55fb17064b0262ff29b459e8493d1785c18ae2553b8", size = 17880549 }, + { url = "https://files.pythonhosted.org/packages/96/30/f7bf4acb5f8db10a96f73896bdeed7a63373137b131ca18bd3dab889db3b/numpy-2.2.3-cp312-cp312-win32.whl", hash = "sha256:4d9828d25fb246bedd31e04c9e75714a4087211ac348cb39c8c5f99dbb6683fe", size = 6293394 }, + { url = "https://files.pythonhosted.org/packages/42/6e/55580a538116d16ae7c9aa17d4edd56e83f42126cb1dfe7a684da7925d2c/numpy-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:83807d445817326b4bcdaaaf8e8e9f1753da04341eceec705c001ff342002e5d", size = 12626357 }, + { url = "https://files.pythonhosted.org/packages/0e/8b/88b98ed534d6a03ba8cddb316950fe80842885709b58501233c29dfa24a9/numpy-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bfdb06b395385ea9b91bf55c1adf1b297c9fdb531552845ff1d3ea6e40d5aba", size = 20916001 }, + { url = "https://files.pythonhosted.org/packages/d9/b4/def6ec32c725cc5fbd8bdf8af80f616acf075fe752d8a23e895da8c67b70/numpy-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:23c9f4edbf4c065fddb10a4f6e8b6a244342d95966a48820c614891e5059bb50", size = 14130721 }, + { url = "https://files.pythonhosted.org/packages/20/60/70af0acc86495b25b672d403e12cb25448d79a2b9658f4fc45e845c397a8/numpy-2.2.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:a0c03b6be48aaf92525cccf393265e02773be8fd9551a2f9adbe7db1fa2b60f1", size = 5130999 }, + { url = "https://files.pythonhosted.org/packages/2e/69/d96c006fb73c9a47bcb3611417cf178049aae159afae47c48bd66df9c536/numpy-2.2.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:2376e317111daa0a6739e50f7ee2a6353f768489102308b0d98fcf4a04f7f3b5", size = 6665299 }, + { url = "https://files.pythonhosted.org/packages/5a/3f/d8a877b6e48103733ac224ffa26b30887dc9944ff95dffdfa6c4ce3d7df3/numpy-2.2.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8fb62fe3d206d72fe1cfe31c4a1106ad2b136fcc1606093aeab314f02930fdf2", size = 14064096 }, + { url = "https://files.pythonhosted.org/packages/e4/43/619c2c7a0665aafc80efca465ddb1f260287266bdbdce517396f2f145d49/numpy-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52659ad2534427dffcc36aac76bebdd02b67e3b7a619ac67543bc9bfe6b7cdb1", size = 16114758 }, + { url = "https://files.pythonhosted.org/packages/d9/79/ee4fe4f60967ccd3897aa71ae14cdee9e3c097e3256975cc9575d393cb42/numpy-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1b416af7d0ed3271cad0f0a0d0bee0911ed7eba23e66f8424d9f3dfcdcae1304", size = 15259880 }, + { url = "https://files.pythonhosted.org/packages/fb/c8/8b55cf05db6d85b7a7d414b3d1bd5a740706df00bfa0824a08bf041e52ee/numpy-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1402da8e0f435991983d0a9708b779f95a8c98c6b18a171b9f1be09005e64d9d", size = 17876721 }, + { url = "https://files.pythonhosted.org/packages/21/d6/b4c2f0564b7dcc413117b0ffbb818d837e4b29996b9234e38b2025ed24e7/numpy-2.2.3-cp313-cp313-win32.whl", hash = "sha256:136553f123ee2951bfcfbc264acd34a2fc2f29d7cdf610ce7daf672b6fbaa693", size = 6290195 }, + { url = "https://files.pythonhosted.org/packages/97/e7/7d55a86719d0de7a6a597949f3febefb1009435b79ba510ff32f05a8c1d7/numpy-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:5b732c8beef1d7bc2d9e476dbba20aaff6167bf205ad9aa8d30913859e82884b", size = 12619013 }, + { url = "https://files.pythonhosted.org/packages/a6/1f/0b863d5528b9048fd486a56e0b97c18bf705e88736c8cea7239012119a54/numpy-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:435e7a933b9fda8126130b046975a968cc2d833b505475e588339e09f7672890", size = 20944621 }, + { url = "https://files.pythonhosted.org/packages/aa/99/b478c384f7a0a2e0736177aafc97dc9152fc036a3fdb13f5a3ab225f1494/numpy-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7678556eeb0152cbd1522b684dcd215250885993dd00adb93679ec3c0e6e091c", size = 14142502 }, + { url = "https://files.pythonhosted.org/packages/fb/61/2d9a694a0f9cd0a839501d362de2a18de75e3004576a3008e56bdd60fcdb/numpy-2.2.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:2e8da03bd561504d9b20e7a12340870dfc206c64ea59b4cfee9fceb95070ee94", size = 5176293 }, + { url = "https://files.pythonhosted.org/packages/33/35/51e94011b23e753fa33f891f601e5c1c9a3d515448659b06df9d40c0aa6e/numpy-2.2.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:c9aa4496fd0e17e3843399f533d62857cef5900facf93e735ef65aa4bbc90ef0", size = 6691874 }, + { url = "https://files.pythonhosted.org/packages/ff/cf/06e37619aad98a9d03bd8d65b8e3041c3a639be0f5f6b0a0e2da544538d4/numpy-2.2.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4ca91d61a4bf61b0f2228f24bbfa6a9facd5f8af03759fe2a655c50ae2c6610", size = 14036826 }, + { url = "https://files.pythonhosted.org/packages/0c/93/5d7d19955abd4d6099ef4a8ee006f9ce258166c38af259f9e5558a172e3e/numpy-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:deaa09cd492e24fd9b15296844c0ad1b3c976da7907e1c1ed3a0ad21dded6f76", size = 16096567 }, + { url = "https://files.pythonhosted.org/packages/af/53/d1c599acf7732d81f46a93621dab6aa8daad914b502a7a115b3f17288ab2/numpy-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:246535e2f7496b7ac85deffe932896a3577be7af8fb7eebe7146444680297e9a", size = 15242514 }, + { url = "https://files.pythonhosted.org/packages/53/43/c0f5411c7b3ea90adf341d05ace762dad8cb9819ef26093e27b15dd121ac/numpy-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:daf43a3d1ea699402c5a850e5313680ac355b4adc9770cd5cfc2940e7861f1bf", size = 17872920 }, + { url = "https://files.pythonhosted.org/packages/5b/57/6dbdd45ab277aff62021cafa1e15f9644a52f5b5fc840bc7591b4079fb58/numpy-2.2.3-cp313-cp313t-win32.whl", hash = "sha256:cf802eef1f0134afb81fef94020351be4fe1d6681aadf9c5e862af6602af64ef", size = 6346584 }, + { url = "https://files.pythonhosted.org/packages/97/9b/484f7d04b537d0a1202a5ba81c6f53f1846ae6c63c2127f8df869ed31342/numpy-2.2.3-cp313-cp313t-win_amd64.whl", hash = "sha256:aee2512827ceb6d7f517c8b85aa5d3923afe8fc7a57d028cffcd522f1c6fd082", size = 12706784 }, +] + +[[package]] +name = "packaging" +version = "24.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, +] + +[[package]] +name = "pandas" +version = "2.2.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy" }, + { name = "python-dateutil" }, + { name = "pytz" }, + { name = "tzdata" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9c/d6/9f8431bacc2e19dca897724cd097b1bb224a6ad5433784a44b587c7c13af/pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667", size = 4399213 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/a3/fb2734118db0af37ea7433f57f722c0a56687e14b14690edff0cdb4b7e58/pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9", size = 12529893 }, + { url = "https://files.pythonhosted.org/packages/e1/0c/ad295fd74bfac85358fd579e271cded3ac969de81f62dd0142c426b9da91/pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4", size = 11363475 }, + { url = "https://files.pythonhosted.org/packages/c6/2a/4bba3f03f7d07207481fed47f5b35f556c7441acddc368ec43d6643c5777/pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3", size = 15188645 }, + { url = "https://files.pythonhosted.org/packages/38/f8/d8fddee9ed0d0c0f4a2132c1dfcf0e3e53265055da8df952a53e7eaf178c/pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319", size = 12739445 }, + { url = "https://files.pythonhosted.org/packages/20/e8/45a05d9c39d2cea61ab175dbe6a2de1d05b679e8de2011da4ee190d7e748/pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8", size = 16359235 }, + { url = "https://files.pythonhosted.org/packages/1d/99/617d07a6a5e429ff90c90da64d428516605a1ec7d7bea494235e1c3882de/pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a", size = 14056756 }, + { url = "https://files.pythonhosted.org/packages/29/d4/1244ab8edf173a10fd601f7e13b9566c1b525c4f365d6bee918e68381889/pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13", size = 11504248 }, + { url = "https://files.pythonhosted.org/packages/64/22/3b8f4e0ed70644e85cfdcd57454686b9057c6c38d2f74fe4b8bc2527214a/pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015", size = 12477643 }, + { url = "https://files.pythonhosted.org/packages/e4/93/b3f5d1838500e22c8d793625da672f3eec046b1a99257666c94446969282/pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28", size = 11281573 }, + { url = "https://files.pythonhosted.org/packages/f5/94/6c79b07f0e5aab1dcfa35a75f4817f5c4f677931d4234afcd75f0e6a66ca/pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0", size = 15196085 }, + { url = "https://files.pythonhosted.org/packages/e8/31/aa8da88ca0eadbabd0a639788a6da13bb2ff6edbbb9f29aa786450a30a91/pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24", size = 12711809 }, + { url = "https://files.pythonhosted.org/packages/ee/7c/c6dbdb0cb2a4344cacfb8de1c5808ca885b2e4dcfde8008266608f9372af/pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659", size = 16356316 }, + { url = "https://files.pythonhosted.org/packages/57/b7/8b757e7d92023b832869fa8881a992696a0bfe2e26f72c9ae9f255988d42/pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb", size = 14022055 }, + { url = "https://files.pythonhosted.org/packages/3b/bc/4b18e2b8c002572c5a441a64826252ce5da2aa738855747247a971988043/pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d", size = 11481175 }, + { url = "https://files.pythonhosted.org/packages/76/a3/a5d88146815e972d40d19247b2c162e88213ef51c7c25993942c39dbf41d/pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468", size = 12615650 }, + { url = "https://files.pythonhosted.org/packages/9c/8c/f0fd18f6140ddafc0c24122c8a964e48294acc579d47def376fef12bcb4a/pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18", size = 11290177 }, + { url = "https://files.pythonhosted.org/packages/ed/f9/e995754eab9c0f14c6777401f7eece0943840b7a9fc932221c19d1abee9f/pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2", size = 14651526 }, + { url = "https://files.pythonhosted.org/packages/25/b0/98d6ae2e1abac4f35230aa756005e8654649d305df9a28b16b9ae4353bff/pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4", size = 11871013 }, + { url = "https://files.pythonhosted.org/packages/cc/57/0f72a10f9db6a4628744c8e8f0df4e6e21de01212c7c981d31e50ffc8328/pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d", size = 15711620 }, + { url = "https://files.pythonhosted.org/packages/ab/5f/b38085618b950b79d2d9164a711c52b10aefc0ae6833b96f626b7021b2ed/pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a", size = 13098436 }, +] + +[[package]] +name = "pillow" +version = "11.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/af/c097e544e7bd278333db77933e535098c259609c4eb3b85381109602fb5b/pillow-11.1.0.tar.gz", hash = "sha256:368da70808b36d73b4b390a8ffac11069f8a5c85f29eff1f1b01bcf3ef5b2a20", size = 46742715 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/95/20/9ce6ed62c91c073fcaa23d216e68289e19d95fb8188b9fb7a63d36771db8/pillow-11.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:2062ffb1d36544d42fcaa277b069c88b01bb7298f4efa06731a7fd6cc290b81a", size = 3226818 }, + { url = "https://files.pythonhosted.org/packages/b9/d8/f6004d98579a2596c098d1e30d10b248798cceff82d2b77aa914875bfea1/pillow-11.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a85b653980faad27e88b141348707ceeef8a1186f75ecc600c395dcac19f385b", size = 3101662 }, + { url = "https://files.pythonhosted.org/packages/08/d9/892e705f90051c7a2574d9f24579c9e100c828700d78a63239676f960b74/pillow-11.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9409c080586d1f683df3f184f20e36fb647f2e0bc3988094d4fd8c9f4eb1b3b3", size = 4329317 }, + { url = "https://files.pythonhosted.org/packages/8c/aa/7f29711f26680eab0bcd3ecdd6d23ed6bce180d82e3f6380fb7ae35fcf3b/pillow-11.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fdadc077553621911f27ce206ffcbec7d3f8d7b50e0da39f10997e8e2bb7f6a", size = 4412999 }, + { url = "https://files.pythonhosted.org/packages/c8/c4/8f0fe3b9e0f7196f6d0bbb151f9fba323d72a41da068610c4c960b16632a/pillow-11.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:93a18841d09bcdd774dcdc308e4537e1f867b3dec059c131fde0327899734aa1", size = 4368819 }, + { url = "https://files.pythonhosted.org/packages/38/0d/84200ed6a871ce386ddc82904bfadc0c6b28b0c0ec78176871a4679e40b3/pillow-11.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9aa9aeddeed452b2f616ff5507459e7bab436916ccb10961c4a382cd3e03f47f", size = 4496081 }, + { url = "https://files.pythonhosted.org/packages/84/9c/9bcd66f714d7e25b64118e3952d52841a4babc6d97b6d28e2261c52045d4/pillow-11.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3cdcdb0b896e981678eee140d882b70092dac83ac1cdf6b3a60e2216a73f2b91", size = 4296513 }, + { url = "https://files.pythonhosted.org/packages/db/61/ada2a226e22da011b45f7104c95ebda1b63dcbb0c378ad0f7c2a710f8fd2/pillow-11.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:36ba10b9cb413e7c7dfa3e189aba252deee0602c86c309799da5a74009ac7a1c", size = 4431298 }, + { url = "https://files.pythonhosted.org/packages/e7/c4/fc6e86750523f367923522014b821c11ebc5ad402e659d8c9d09b3c9d70c/pillow-11.1.0-cp312-cp312-win32.whl", hash = "sha256:cfd5cd998c2e36a862d0e27b2df63237e67273f2fc78f47445b14e73a810e7e6", size = 2291630 }, + { url = "https://files.pythonhosted.org/packages/08/5c/2104299949b9d504baf3f4d35f73dbd14ef31bbd1ddc2c1b66a5b7dfda44/pillow-11.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:a697cd8ba0383bba3d2d3ada02b34ed268cb548b369943cd349007730c92bddf", size = 2626369 }, + { url = "https://files.pythonhosted.org/packages/37/f3/9b18362206b244167c958984b57c7f70a0289bfb59a530dd8af5f699b910/pillow-11.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:4dd43a78897793f60766563969442020e90eb7847463eca901e41ba186a7d4a5", size = 2375240 }, + { url = "https://files.pythonhosted.org/packages/b3/31/9ca79cafdce364fd5c980cd3416c20ce1bebd235b470d262f9d24d810184/pillow-11.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ae98e14432d458fc3de11a77ccb3ae65ddce70f730e7c76140653048c71bfcbc", size = 3226640 }, + { url = "https://files.pythonhosted.org/packages/ac/0f/ff07ad45a1f172a497aa393b13a9d81a32e1477ef0e869d030e3c1532521/pillow-11.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cc1331b6d5a6e144aeb5e626f4375f5b7ae9934ba620c0ac6b3e43d5e683a0f0", size = 3101437 }, + { url = "https://files.pythonhosted.org/packages/08/2f/9906fca87a68d29ec4530be1f893149e0cb64a86d1f9f70a7cfcdfe8ae44/pillow-11.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:758e9d4ef15d3560214cddbc97b8ef3ef86ce04d62ddac17ad39ba87e89bd3b1", size = 4326605 }, + { url = "https://files.pythonhosted.org/packages/b0/0f/f3547ee15b145bc5c8b336401b2d4c9d9da67da9dcb572d7c0d4103d2c69/pillow-11.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b523466b1a31d0dcef7c5be1f20b942919b62fd6e9a9be199d035509cbefc0ec", size = 4411173 }, + { url = "https://files.pythonhosted.org/packages/b1/df/bf8176aa5db515c5de584c5e00df9bab0713548fd780c82a86cba2c2fedb/pillow-11.1.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:9044b5e4f7083f209c4e35aa5dd54b1dd5b112b108648f5c902ad586d4f945c5", size = 4369145 }, + { url = "https://files.pythonhosted.org/packages/de/7c/7433122d1cfadc740f577cb55526fdc39129a648ac65ce64db2eb7209277/pillow-11.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:3764d53e09cdedd91bee65c2527815d315c6b90d7b8b79759cc48d7bf5d4f114", size = 4496340 }, + { url = "https://files.pythonhosted.org/packages/25/46/dd94b93ca6bd555588835f2504bd90c00d5438fe131cf01cfa0c5131a19d/pillow-11.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:31eba6bbdd27dde97b0174ddf0297d7a9c3a507a8a1480e1e60ef914fe23d352", size = 4296906 }, + { url = "https://files.pythonhosted.org/packages/a8/28/2f9d32014dfc7753e586db9add35b8a41b7a3b46540e965cb6d6bc607bd2/pillow-11.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b5d658fbd9f0d6eea113aea286b21d3cd4d3fd978157cbf2447a6035916506d3", size = 4431759 }, + { url = "https://files.pythonhosted.org/packages/33/48/19c2cbe7403870fbe8b7737d19eb013f46299cdfe4501573367f6396c775/pillow-11.1.0-cp313-cp313-win32.whl", hash = "sha256:f86d3a7a9af5d826744fabf4afd15b9dfef44fe69a98541f666f66fbb8d3fef9", size = 2291657 }, + { url = "https://files.pythonhosted.org/packages/3b/ad/285c556747d34c399f332ba7c1a595ba245796ef3e22eae190f5364bb62b/pillow-11.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:593c5fd6be85da83656b93ffcccc2312d2d149d251e98588b14fbc288fd8909c", size = 2626304 }, + { url = "https://files.pythonhosted.org/packages/e5/7b/ef35a71163bf36db06e9c8729608f78dedf032fc8313d19bd4be5c2588f3/pillow-11.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:11633d58b6ee5733bde153a8dafd25e505ea3d32e261accd388827ee987baf65", size = 2375117 }, + { url = "https://files.pythonhosted.org/packages/79/30/77f54228401e84d6791354888549b45824ab0ffde659bafa67956303a09f/pillow-11.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:70ca5ef3b3b1c4a0812b5c63c57c23b63e53bc38e758b37a951e5bc466449861", size = 3230060 }, + { url = "https://files.pythonhosted.org/packages/ce/b1/56723b74b07dd64c1010fee011951ea9c35a43d8020acd03111f14298225/pillow-11.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8000376f139d4d38d6851eb149b321a52bb8893a88dae8ee7d95840431977081", size = 3106192 }, + { url = "https://files.pythonhosted.org/packages/e1/cd/7bf7180e08f80a4dcc6b4c3a0aa9e0b0ae57168562726a05dc8aa8fa66b0/pillow-11.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ee85f0696a17dd28fbcfceb59f9510aa71934b483d1f5601d1030c3c8304f3c", size = 4446805 }, + { url = "https://files.pythonhosted.org/packages/97/42/87c856ea30c8ed97e8efbe672b58c8304dee0573f8c7cab62ae9e31db6ae/pillow-11.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:dd0e081319328928531df7a0e63621caf67652c8464303fd102141b785ef9547", size = 4530623 }, + { url = "https://files.pythonhosted.org/packages/ff/41/026879e90c84a88e33fb00cc6bd915ac2743c67e87a18f80270dfe3c2041/pillow-11.1.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e63e4e5081de46517099dc30abe418122f54531a6ae2ebc8680bcd7096860eab", size = 4465191 }, + { url = "https://files.pythonhosted.org/packages/e5/fb/a7960e838bc5df57a2ce23183bfd2290d97c33028b96bde332a9057834d3/pillow-11.1.0-cp313-cp313t-win32.whl", hash = "sha256:dda60aa465b861324e65a78c9f5cf0f4bc713e4309f83bc387be158b077963d9", size = 2295494 }, + { url = "https://files.pythonhosted.org/packages/d7/6c/6ec83ee2f6f0fda8d4cf89045c6be4b0373ebfc363ba8538f8c999f63fcd/pillow-11.1.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ad5db5781c774ab9a9b2c4302bbf0c1014960a0a7be63278d13ae6fdf88126fe", size = 2631595 }, + { url = "https://files.pythonhosted.org/packages/cf/6c/41c21c6c8af92b9fea313aa47c75de49e2f9a467964ee33eb0135d47eb64/pillow-11.1.0-cp313-cp313t-win_arm64.whl", hash = "sha256:67cd427c68926108778a9005f2a04adbd5e67c442ed21d95389fe1d595458756", size = 2377651 }, +] + +[[package]] +name = "pint" +version = "0.24.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "flexcache" }, + { name = "flexparser" }, + { name = "platformdirs" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/20/bb/52b15ddf7b7706ed591134a895dbf6e41c8348171fb635e655e0a4bbb0ea/pint-0.24.4.tar.gz", hash = "sha256:35275439b574837a6cd3020a5a4a73645eb125ce4152a73a2f126bf164b91b80", size = 342225 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/16/bd2f5904557265882108dc2e04f18abc05ab0c2b7082ae9430091daf1d5c/Pint-0.24.4-py3-none-any.whl", hash = "sha256:aa54926c8772159fcf65f82cc0d34de6768c151b32ad1deb0331291c38fe7659", size = 302029 }, +] + +[[package]] +name = "platformdirs" +version = "4.3.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/fc/128cc9cb8f03208bdbf93d3aa862e16d376844a14f9a0ce5cf4507372de4/platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", size = 21302 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/a6/bc1012356d8ece4d66dd75c4b9fc6c1f6650ddd5991e421177d9f8f671be/platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb", size = 18439 }, +] + +[[package]] +name = "plotly" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "narwhals" }, + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/9c/80/761c14012d6daf18e12b6d1e4f6b218e999bcceb694d7a9b180154f9e4db/plotly-6.0.0.tar.gz", hash = "sha256:c4aad38b8c3d65e4a5e7dd308b084143b9025c2cc9d5317fc1f1d30958db87d3", size = 8111782 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/77/a946f38b57fb88e736c71fbdd737a1aebd27b532bda0779c137f357cf5fc/plotly-6.0.0-py3-none-any.whl", hash = "sha256:f708871c3a9349a68791ff943a5781b1ec04de7769ea69068adcd9202e57653a", size = 14805949 }, +] + +[[package]] +name = "protobuf" +version = "5.29.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f7/d1/e0a911544ca9993e0f17ce6d3cc0932752356c1b0a834397f28e63479344/protobuf-5.29.3.tar.gz", hash = "sha256:5da0f41edaf117bde316404bad1a486cb4ededf8e4a54891296f648e8e076620", size = 424945 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/7a/1e38f3cafa022f477ca0f57a1f49962f21ad25850c3ca0acd3b9d0091518/protobuf-5.29.3-cp310-abi3-win32.whl", hash = "sha256:3ea51771449e1035f26069c4c7fd51fba990d07bc55ba80701c78f886bf9c888", size = 422708 }, + { url = "https://files.pythonhosted.org/packages/61/fa/aae8e10512b83de633f2646506a6d835b151edf4b30d18d73afd01447253/protobuf-5.29.3-cp310-abi3-win_amd64.whl", hash = "sha256:a4fa6f80816a9a0678429e84973f2f98cbc218cca434abe8db2ad0bffc98503a", size = 434508 }, + { url = "https://files.pythonhosted.org/packages/dd/04/3eaedc2ba17a088961d0e3bd396eac764450f431621b58a04ce898acd126/protobuf-5.29.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:a8434404bbf139aa9e1300dbf989667a83d42ddda9153d8ab76e0d5dcaca484e", size = 417825 }, + { url = "https://files.pythonhosted.org/packages/4f/06/7c467744d23c3979ce250397e26d8ad8eeb2bea7b18ca12ad58313c1b8d5/protobuf-5.29.3-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:daaf63f70f25e8689c072cfad4334ca0ac1d1e05a92fc15c54eb9cf23c3efd84", size = 319573 }, + { url = "https://files.pythonhosted.org/packages/a8/45/2ebbde52ad2be18d3675b6bee50e68cd73c9e0654de77d595540b5129df8/protobuf-5.29.3-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:c027e08a08be10b67c06bf2370b99c811c466398c357e615ca88c91c07f0910f", size = 319672 }, + { url = "https://files.pythonhosted.org/packages/fd/b2/ab07b09e0f6d143dfb839693aa05765257bceaa13d03bf1a696b78323e7a/protobuf-5.29.3-py3-none-any.whl", hash = "sha256:0a18ed4a24198528f2333802eb075e59dea9d679ab7a6c5efb017a59004d849f", size = 172550 }, +] + +[[package]] +name = "pyarrow" +version = "19.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7f/09/a9046344212690f0632b9c709f9bf18506522feb333c894d0de81d62341a/pyarrow-19.0.1.tar.gz", hash = "sha256:3bf266b485df66a400f282ac0b6d1b500b9d2ae73314a153dbe97d6d5cc8a99e", size = 1129437 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b4/94e828704b050e723f67d67c3535cf7076c7432cd4cf046e4bb3b96a9c9d/pyarrow-19.0.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:80b2ad2b193e7d19e81008a96e313fbd53157945c7be9ac65f44f8937a55427b", size = 30670749 }, + { url = "https://files.pythonhosted.org/packages/7e/3b/4692965e04bb1df55e2c314c4296f1eb12b4f3052d4cf43d29e076aedf66/pyarrow-19.0.1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:ee8dec072569f43835932a3b10c55973593abc00936c202707a4ad06af7cb294", size = 32128007 }, + { url = "https://files.pythonhosted.org/packages/22/f7/2239af706252c6582a5635c35caa17cb4d401cd74a87821ef702e3888957/pyarrow-19.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d5d1ec7ec5324b98887bdc006f4d2ce534e10e60f7ad995e7875ffa0ff9cb14", size = 41144566 }, + { url = "https://files.pythonhosted.org/packages/fb/e3/c9661b2b2849cfefddd9fd65b64e093594b231b472de08ff658f76c732b2/pyarrow-19.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3ad4c0eb4e2a9aeb990af6c09e6fa0b195c8c0e7b272ecc8d4d2b6574809d34", size = 42202991 }, + { url = "https://files.pythonhosted.org/packages/fe/4f/a2c0ed309167ef436674782dfee4a124570ba64299c551e38d3fdaf0a17b/pyarrow-19.0.1-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:d383591f3dcbe545f6cc62daaef9c7cdfe0dff0fb9e1c8121101cabe9098cfa6", size = 40507986 }, + { url = "https://files.pythonhosted.org/packages/27/2e/29bb28a7102a6f71026a9d70d1d61df926887e36ec797f2e6acfd2dd3867/pyarrow-19.0.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:b4c4156a625f1e35d6c0b2132635a237708944eb41df5fbe7d50f20d20c17832", size = 42087026 }, + { url = "https://files.pythonhosted.org/packages/16/33/2a67c0f783251106aeeee516f4806161e7b481f7d744d0d643d2f30230a5/pyarrow-19.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:5bd1618ae5e5476b7654c7b55a6364ae87686d4724538c24185bbb2952679960", size = 25250108 }, + { url = "https://files.pythonhosted.org/packages/2b/8d/275c58d4b00781bd36579501a259eacc5c6dfb369be4ddeb672ceb551d2d/pyarrow-19.0.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:e45274b20e524ae5c39d7fc1ca2aa923aab494776d2d4b316b49ec7572ca324c", size = 30653552 }, + { url = "https://files.pythonhosted.org/packages/a0/9e/e6aca5cc4ef0c7aec5f8db93feb0bde08dbad8c56b9014216205d271101b/pyarrow-19.0.1-cp313-cp313-macosx_12_0_x86_64.whl", hash = "sha256:d9dedeaf19097a143ed6da37f04f4051aba353c95ef507764d344229b2b740ae", size = 32103413 }, + { url = "https://files.pythonhosted.org/packages/6a/fa/a7033f66e5d4f1308c7eb0dfcd2ccd70f881724eb6fd1776657fdf65458f/pyarrow-19.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ebfb5171bb5f4a52319344ebbbecc731af3f021e49318c74f33d520d31ae0c4", size = 41134869 }, + { url = "https://files.pythonhosted.org/packages/2d/92/34d2569be8e7abdc9d145c98dc410db0071ac579b92ebc30da35f500d630/pyarrow-19.0.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2a21d39fbdb948857f67eacb5bbaaf36802de044ec36fbef7a1c8f0dd3a4ab2", size = 42192626 }, + { url = "https://files.pythonhosted.org/packages/0a/1f/80c617b1084fc833804dc3309aa9d8daacd46f9ec8d736df733f15aebe2c/pyarrow-19.0.1-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:99bc1bec6d234359743b01e70d4310d0ab240c3d6b0da7e2a93663b0158616f6", size = 40496708 }, + { url = "https://files.pythonhosted.org/packages/e6/90/83698fcecf939a611c8d9a78e38e7fed7792dcc4317e29e72cf8135526fb/pyarrow-19.0.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:1b93ef2c93e77c442c979b0d596af45e4665d8b96da598db145b0fec014b9136", size = 42075728 }, + { url = "https://files.pythonhosted.org/packages/40/49/2325f5c9e7a1c125c01ba0c509d400b152c972a47958768e4e35e04d13d8/pyarrow-19.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:d9d46e06846a41ba906ab25302cf0fd522f81aa2a85a71021826f34639ad31ef", size = 25242568 }, + { url = "https://files.pythonhosted.org/packages/3f/72/135088d995a759d4d916ec4824cb19e066585b4909ebad4ab196177aa825/pyarrow-19.0.1-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:c0fe3dbbf054a00d1f162fda94ce236a899ca01123a798c561ba307ca38af5f0", size = 30702371 }, + { url = "https://files.pythonhosted.org/packages/2e/01/00beeebd33d6bac701f20816a29d2018eba463616bbc07397fdf99ac4ce3/pyarrow-19.0.1-cp313-cp313t-macosx_12_0_x86_64.whl", hash = "sha256:96606c3ba57944d128e8a8399da4812f56c7f61de8c647e3470b417f795d0ef9", size = 32116046 }, + { url = "https://files.pythonhosted.org/packages/1f/c9/23b1ea718dfe967cbd986d16cf2a31fe59d015874258baae16d7ea0ccabc/pyarrow-19.0.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f04d49a6b64cf24719c080b3c2029a3a5b16417fd5fd7c4041f94233af732f3", size = 41091183 }, + { url = "https://files.pythonhosted.org/packages/3a/d4/b4a3aa781a2c715520aa8ab4fe2e7fa49d33a1d4e71c8fc6ab7b5de7a3f8/pyarrow-19.0.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a9137cf7e1640dce4c190551ee69d478f7121b5c6f323553b319cac936395f6", size = 42171896 }, + { url = "https://files.pythonhosted.org/packages/23/1b/716d4cd5a3cbc387c6e6745d2704c4b46654ba2668260d25c402626c5ddb/pyarrow-19.0.1-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:7c1bca1897c28013db5e4c83944a2ab53231f541b9e0c3f4791206d0c0de389a", size = 40464851 }, + { url = "https://files.pythonhosted.org/packages/ed/bd/54907846383dcc7ee28772d7e646f6c34276a17da740002a5cefe90f04f7/pyarrow-19.0.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:58d9397b2e273ef76264b45531e9d552d8ec8a6688b7390b5be44c02a37aade8", size = 42085744 }, +] + +[[package]] +name = "pydantic" +version = "2.10.6" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b7/ae/d5220c5c52b158b1de7ca89fc5edb72f304a70a4c540c84c8844bf4008de/pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236", size = 761681 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/3c/8cc1cc84deffa6e25d2d0c688ebb80635dfdbf1dbea3e30c541c8cf4d860/pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584", size = 431696 }, +] + +[[package]] +name = "pydantic-core" +version = "2.27.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/fc/01/f3e5ac5e7c25833db5eb555f7b7ab24cd6f8c322d3a3ad2d67a952dc0abc/pydantic_core-2.27.2.tar.gz", hash = "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39", size = 413443 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d6/74/51c8a5482ca447871c93e142d9d4a92ead74de6c8dc5e66733e22c9bba89/pydantic_core-2.27.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0", size = 1893127 }, + { url = "https://files.pythonhosted.org/packages/d3/f3/c97e80721735868313c58b89d2de85fa80fe8dfeeed84dc51598b92a135e/pydantic_core-2.27.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef", size = 1811340 }, + { url = "https://files.pythonhosted.org/packages/9e/91/840ec1375e686dbae1bd80a9e46c26a1e0083e1186abc610efa3d9a36180/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7", size = 1822900 }, + { url = "https://files.pythonhosted.org/packages/f6/31/4240bc96025035500c18adc149aa6ffdf1a0062a4b525c932065ceb4d868/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934", size = 1869177 }, + { url = "https://files.pythonhosted.org/packages/fa/20/02fbaadb7808be578317015c462655c317a77a7c8f0ef274bc016a784c54/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6", size = 2038046 }, + { url = "https://files.pythonhosted.org/packages/06/86/7f306b904e6c9eccf0668248b3f272090e49c275bc488a7b88b0823444a4/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c", size = 2685386 }, + { url = "https://files.pythonhosted.org/packages/8d/f0/49129b27c43396581a635d8710dae54a791b17dfc50c70164866bbf865e3/pydantic_core-2.27.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2", size = 1997060 }, + { url = "https://files.pythonhosted.org/packages/0d/0f/943b4af7cd416c477fd40b187036c4f89b416a33d3cc0ab7b82708a667aa/pydantic_core-2.27.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4", size = 2004870 }, + { url = "https://files.pythonhosted.org/packages/35/40/aea70b5b1a63911c53a4c8117c0a828d6790483f858041f47bab0b779f44/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3", size = 1999822 }, + { url = "https://files.pythonhosted.org/packages/f2/b3/807b94fd337d58effc5498fd1a7a4d9d59af4133e83e32ae39a96fddec9d/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4", size = 2130364 }, + { url = "https://files.pythonhosted.org/packages/fc/df/791c827cd4ee6efd59248dca9369fb35e80a9484462c33c6649a8d02b565/pydantic_core-2.27.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57", size = 2158303 }, + { url = "https://files.pythonhosted.org/packages/9b/67/4e197c300976af185b7cef4c02203e175fb127e414125916bf1128b639a9/pydantic_core-2.27.2-cp312-cp312-win32.whl", hash = "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc", size = 1834064 }, + { url = "https://files.pythonhosted.org/packages/1f/ea/cd7209a889163b8dcca139fe32b9687dd05249161a3edda62860430457a5/pydantic_core-2.27.2-cp312-cp312-win_amd64.whl", hash = "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9", size = 1989046 }, + { url = "https://files.pythonhosted.org/packages/bc/49/c54baab2f4658c26ac633d798dab66b4c3a9bbf47cff5284e9c182f4137a/pydantic_core-2.27.2-cp312-cp312-win_arm64.whl", hash = "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b", size = 1885092 }, + { url = "https://files.pythonhosted.org/packages/41/b1/9bc383f48f8002f99104e3acff6cba1231b29ef76cfa45d1506a5cad1f84/pydantic_core-2.27.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b", size = 1892709 }, + { url = "https://files.pythonhosted.org/packages/10/6c/e62b8657b834f3eb2961b49ec8e301eb99946245e70bf42c8817350cbefc/pydantic_core-2.27.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154", size = 1811273 }, + { url = "https://files.pythonhosted.org/packages/ba/15/52cfe49c8c986e081b863b102d6b859d9defc63446b642ccbbb3742bf371/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9", size = 1823027 }, + { url = "https://files.pythonhosted.org/packages/b1/1c/b6f402cfc18ec0024120602bdbcebc7bdd5b856528c013bd4d13865ca473/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9", size = 1868888 }, + { url = "https://files.pythonhosted.org/packages/bd/7b/8cb75b66ac37bc2975a3b7de99f3c6f355fcc4d89820b61dffa8f1e81677/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1", size = 2037738 }, + { url = "https://files.pythonhosted.org/packages/c8/f1/786d8fe78970a06f61df22cba58e365ce304bf9b9f46cc71c8c424e0c334/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a", size = 2685138 }, + { url = "https://files.pythonhosted.org/packages/a6/74/d12b2cd841d8724dc8ffb13fc5cef86566a53ed358103150209ecd5d1999/pydantic_core-2.27.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e", size = 1997025 }, + { url = "https://files.pythonhosted.org/packages/a0/6e/940bcd631bc4d9a06c9539b51f070b66e8f370ed0933f392db6ff350d873/pydantic_core-2.27.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4", size = 2004633 }, + { url = "https://files.pythonhosted.org/packages/50/cc/a46b34f1708d82498c227d5d80ce615b2dd502ddcfd8376fc14a36655af1/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27", size = 1999404 }, + { url = "https://files.pythonhosted.org/packages/ca/2d/c365cfa930ed23bc58c41463bae347d1005537dc8db79e998af8ba28d35e/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee", size = 2130130 }, + { url = "https://files.pythonhosted.org/packages/f4/d7/eb64d015c350b7cdb371145b54d96c919d4db516817f31cd1c650cae3b21/pydantic_core-2.27.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1", size = 2157946 }, + { url = "https://files.pythonhosted.org/packages/a4/99/bddde3ddde76c03b65dfd5a66ab436c4e58ffc42927d4ff1198ffbf96f5f/pydantic_core-2.27.2-cp313-cp313-win32.whl", hash = "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130", size = 1834387 }, + { url = "https://files.pythonhosted.org/packages/71/47/82b5e846e01b26ac6f1893d3c5f9f3a2eb6ba79be26eef0b759b4fe72946/pydantic_core-2.27.2-cp313-cp313-win_amd64.whl", hash = "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee", size = 1990453 }, + { url = "https://files.pythonhosted.org/packages/51/b2/b2b50d5ecf21acf870190ae5d093602d95f66c9c31f9d5de6062eb329ad1/pydantic_core-2.27.2-cp313-cp313-win_arm64.whl", hash = "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b", size = 1885186 }, +] + +[[package]] +name = "pydeck" +version = "0.9.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jinja2" }, + { name = "numpy" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a1/ca/40e14e196864a0f61a92abb14d09b3d3da98f94ccb03b49cf51688140dab/pydeck-0.9.1.tar.gz", hash = "sha256:f74475ae637951d63f2ee58326757f8d4f9cd9f2a457cf42950715003e2cb605", size = 3832240 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/4c/b888e6cf58bd9db9c93f40d1c6be8283ff49d88919231afe93a6bcf61626/pydeck-0.9.1-py2.py3-none-any.whl", hash = "sha256:b3f75ba0d273fc917094fa61224f3f6076ca8752b93d46faf3bcfd9f9d59b038", size = 6900403 }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892 }, +] + +[[package]] +name = "pytz" +version = "2025.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/57/df1c9157c8d5a05117e455d66fd7cf6dbc46974f832b1058ed4856785d8a/pytz-2025.1.tar.gz", hash = "sha256:c2db42be2a2518b28e65f9207c4d05e6ff547d1efa4086469ef855e4ab70178e", size = 319617 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/eb/38/ac33370d784287baa1c3d538978b5e2ea064d4c1b93ffbd12826c190dd10/pytz-2025.1-py2.py3-none-any.whl", hash = "sha256:89dd22dca55b46eac6eda23b2d72721bf1bdfef212645d81513ef5d03038de57", size = 507930 }, +] + +[[package]] +name = "referencing" +version = "0.36.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "rpds-py" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/2f/db/98b5c277be99dd18bfd91dd04e1b759cad18d1a338188c936e92f921c7e2/referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", size = 74744 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/b1/3baf80dc6d2b7bc27a95a67752d0208e410351e3feb4eb78de5f77454d8d/referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0", size = 26775 }, +] + +[[package]] +name = "requests" +version = "2.32.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", size = 131218 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6", size = 64928 }, +] + +[[package]] +name = "rpds-py" +version = "0.23.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0a/79/2ce611b18c4fd83d9e3aecb5cba93e1917c050f556db39842889fa69b79f/rpds_py-0.23.1.tar.gz", hash = "sha256:7f3240dcfa14d198dba24b8b9cb3b108c06b68d45b7babd9eefc1038fdf7e707", size = 26806 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/8c/d17efccb9f5b9137ddea706664aebae694384ae1d5997c0202093e37185a/rpds_py-0.23.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3902df19540e9af4cc0c3ae75974c65d2c156b9257e91f5101a51f99136d834c", size = 364369 }, + { url = "https://files.pythonhosted.org/packages/6e/c0/ab030f696b5c573107115a88d8d73d80f03309e60952b64c584c70c659af/rpds_py-0.23.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:66f8d2a17e5838dd6fb9be6baaba8e75ae2f5fa6b6b755d597184bfcd3cb0eba", size = 349965 }, + { url = "https://files.pythonhosted.org/packages/b3/55/b40170f5a079c4fb0b6a82b299689e66e744edca3c3375a8b160fb797660/rpds_py-0.23.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:112b8774b0b4ee22368fec42749b94366bd9b536f8f74c3d4175d4395f5cbd31", size = 389064 }, + { url = "https://files.pythonhosted.org/packages/ab/1c/b03a912c59ec7c1e16b26e587b9dfa8ddff3b07851e781e8c46e908a365a/rpds_py-0.23.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e0df046f2266e8586cf09d00588302a32923eb6386ced0ca5c9deade6af9a149", size = 397741 }, + { url = "https://files.pythonhosted.org/packages/52/6f/151b90792b62fb6f87099bcc9044c626881fdd54e31bf98541f830b15cea/rpds_py-0.23.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0f3288930b947cbebe767f84cf618d2cbe0b13be476e749da0e6a009f986248c", size = 448784 }, + { url = "https://files.pythonhosted.org/packages/71/2a/6de67c0c97ec7857e0e9e5cd7c52405af931b303eb1e5b9eff6c50fd9a2e/rpds_py-0.23.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce473a2351c018b06dd8d30d5da8ab5a0831056cc53b2006e2a8028172c37ce5", size = 440203 }, + { url = "https://files.pythonhosted.org/packages/db/5e/e759cd1c276d98a4b1f464b17a9bf66c65d29f8f85754e27e1467feaa7c3/rpds_py-0.23.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d550d7e9e7d8676b183b37d65b5cd8de13676a738973d330b59dc8312df9c5dc", size = 391611 }, + { url = "https://files.pythonhosted.org/packages/1c/1e/2900358efcc0d9408c7289769cba4c0974d9db314aa884028ed7f7364f61/rpds_py-0.23.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e14f86b871ea74c3fddc9a40e947d6a5d09def5adc2076ee61fb910a9014fb35", size = 423306 }, + { url = "https://files.pythonhosted.org/packages/23/07/6c177e6d059f5d39689352d6c69a926ee4805ffdb6f06203570234d3d8f7/rpds_py-0.23.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1bf5be5ba34e19be579ae873da515a2836a2166d8d7ee43be6ff909eda42b72b", size = 562323 }, + { url = "https://files.pythonhosted.org/packages/70/e4/f9097fd1c02b516fff9850792161eb9fc20a2fd54762f3c69eae0bdb67cb/rpds_py-0.23.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d7031d493c4465dbc8d40bd6cafefef4bd472b17db0ab94c53e7909ee781b9ef", size = 588351 }, + { url = "https://files.pythonhosted.org/packages/87/39/5db3c6f326bfbe4576ae2af6435bd7555867d20ae690c786ff33659f293b/rpds_py-0.23.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:55ff4151cfd4bc635e51cfb1c59ac9f7196b256b12e3a57deb9e5742e65941ad", size = 557252 }, + { url = "https://files.pythonhosted.org/packages/fd/14/2d5ad292f144fa79bafb78d2eb5b8a3a91c358b6065443cb9c49b5d1fedf/rpds_py-0.23.1-cp312-cp312-win32.whl", hash = "sha256:a9d3b728f5a5873d84cba997b9d617c6090ca5721caaa691f3b1a78c60adc057", size = 222181 }, + { url = "https://files.pythonhosted.org/packages/a3/4f/0fce63e0f5cdd658e71e21abd17ac1bc9312741ebb8b3f74eeed2ebdf771/rpds_py-0.23.1-cp312-cp312-win_amd64.whl", hash = "sha256:b03a8d50b137ee758e4c73638b10747b7c39988eb8e6cd11abb7084266455165", size = 237426 }, + { url = "https://files.pythonhosted.org/packages/13/9d/b8b2c0edffb0bed15be17b6d5ab06216f2f47f9ee49259c7e96a3ad4ca42/rpds_py-0.23.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:4caafd1a22e5eaa3732acb7672a497123354bef79a9d7ceed43387d25025e935", size = 363672 }, + { url = "https://files.pythonhosted.org/packages/bd/c2/5056fa29e6894144d7ba4c938b9b0445f75836b87d2dd00ed4999dc45a8c/rpds_py-0.23.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:178f8a60fc24511c0eb756af741c476b87b610dba83270fce1e5a430204566a4", size = 349602 }, + { url = "https://files.pythonhosted.org/packages/b0/bc/33779a1bb0ee32d8d706b173825aab75c628521d23ce72a7c1e6a6852f86/rpds_py-0.23.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c632419c3870507ca20a37c8f8f5352317aca097639e524ad129f58c125c61c6", size = 388746 }, + { url = "https://files.pythonhosted.org/packages/62/0b/71db3e36b7780a619698ec82a9c87ab44ad7ca7f5480913e8a59ff76f050/rpds_py-0.23.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:698a79d295626ee292d1730bc2ef6e70a3ab135b1d79ada8fde3ed0047b65a10", size = 397076 }, + { url = "https://files.pythonhosted.org/packages/bb/2e/494398f613edf77ba10a916b1ddea2acce42ab0e3b62e2c70ffc0757ce00/rpds_py-0.23.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:271fa2184cf28bdded86bb6217c8e08d3a169fe0bbe9be5e8d96e8476b707122", size = 448399 }, + { url = "https://files.pythonhosted.org/packages/dd/53/4bd7f5779b1f463243ee5fdc83da04dd58a08f86e639dbffa7a35f969a84/rpds_py-0.23.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b91cceb5add79ee563bd1f70b30896bd63bc5f78a11c1f00a1e931729ca4f1f4", size = 439764 }, + { url = "https://files.pythonhosted.org/packages/f6/55/b3c18c04a460d951bf8e91f2abf46ce5b6426fb69784166a6a25827cb90a/rpds_py-0.23.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a6cb95074777f1ecda2ca4fa7717caa9ee6e534f42b7575a8f0d4cb0c24013", size = 390662 }, + { url = "https://files.pythonhosted.org/packages/2a/65/cc463044a3cbd616029b2aa87a651cdee8288d2fdd7780b2244845e934c1/rpds_py-0.23.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:50fb62f8d8364978478b12d5f03bf028c6bc2af04082479299139dc26edf4c64", size = 422680 }, + { url = "https://files.pythonhosted.org/packages/fa/8e/1fa52990c7836d72e8d70cd7753f2362c72fbb0a49c1462e8c60e7176d0b/rpds_py-0.23.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c8f7e90b948dc9dcfff8003f1ea3af08b29c062f681c05fd798e36daa3f7e3e8", size = 561792 }, + { url = "https://files.pythonhosted.org/packages/57/b8/fe3b612979b1a29d0c77f8585903d8b3a292604b26d4b300e228b8ac6360/rpds_py-0.23.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:5b98b6c953e5c2bda51ab4d5b4f172617d462eebc7f4bfdc7c7e6b423f6da957", size = 588127 }, + { url = "https://files.pythonhosted.org/packages/44/2d/fde474de516bbc4b9b230f43c98e7f8acc5da7fc50ceed8e7af27553d346/rpds_py-0.23.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2893d778d4671ee627bac4037a075168b2673c57186fb1a57e993465dbd79a93", size = 556981 }, + { url = "https://files.pythonhosted.org/packages/18/57/767deeb27b81370bbab8f74ef6e68d26c4ea99018f3c71a570e506fede85/rpds_py-0.23.1-cp313-cp313-win32.whl", hash = "sha256:2cfa07c346a7ad07019c33fb9a63cf3acb1f5363c33bc73014e20d9fe8b01cdd", size = 221936 }, + { url = "https://files.pythonhosted.org/packages/7d/6c/3474cfdd3cafe243f97ab8474ea8949236eb2a1a341ca55e75ce00cd03da/rpds_py-0.23.1-cp313-cp313-win_amd64.whl", hash = "sha256:3aaf141d39f45322e44fc2c742e4b8b4098ead5317e5f884770c8df0c332da70", size = 237145 }, + { url = "https://files.pythonhosted.org/packages/ec/77/e985064c624230f61efa0423759bb066da56ebe40c654f8b5ba225bd5d63/rpds_py-0.23.1-cp313-cp313t-macosx_10_12_x86_64.whl", hash = "sha256:759462b2d0aa5a04be5b3e37fb8183615f47014ae6b116e17036b131985cb731", size = 359623 }, + { url = "https://files.pythonhosted.org/packages/62/d9/a33dcbf62b29e40559e012d525bae7d516757cf042cc9234bd34ca4b6aeb/rpds_py-0.23.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3e9212f52074fc9d72cf242a84063787ab8e21e0950d4d6709886fb62bcb91d5", size = 345900 }, + { url = "https://files.pythonhosted.org/packages/92/eb/f81a4be6397861adb2cb868bb6a28a33292c2dcac567d1dc575226055e55/rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e9f3a3ac919406bc0414bbbd76c6af99253c507150191ea79fab42fdb35982a", size = 386426 }, + { url = "https://files.pythonhosted.org/packages/09/47/1f810c9b5e83be005341201b5389f1d240dfa440346ea7189f9b3fd6961d/rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c04ca91dda8a61584165825907f5c967ca09e9c65fe8966ee753a3f2b019fe1e", size = 392314 }, + { url = "https://files.pythonhosted.org/packages/83/bd/bc95831432fd6c46ed8001f01af26de0763a059d6d7e6d69e3c5bf02917a/rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ab923167cfd945abb9b51a407407cf19f5bee35001221f2911dc85ffd35ff4f", size = 447706 }, + { url = "https://files.pythonhosted.org/packages/19/3e/567c04c226b1802dc6dc82cad3d53e1fa0a773258571c74ac5d8fbde97ed/rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ed6f011bedca8585787e5082cce081bac3d30f54520097b2411351b3574e1219", size = 437060 }, + { url = "https://files.pythonhosted.org/packages/fe/77/a77d2c6afe27ae7d0d55fc32f6841502648070dc8d549fcc1e6d47ff8975/rpds_py-0.23.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6959bb9928c5c999aba4a3f5a6799d571ddc2c59ff49917ecf55be2bbb4e3722", size = 389347 }, + { url = "https://files.pythonhosted.org/packages/3f/47/6b256ff20a74cfebeac790ab05586e0ac91f88e331125d4740a6c86fc26f/rpds_py-0.23.1-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1ed7de3c86721b4e83ac440751329ec6a1102229aa18163f84c75b06b525ad7e", size = 415554 }, + { url = "https://files.pythonhosted.org/packages/fc/29/d4572469a245bc9fc81e35166dca19fc5298d5c43e1a6dd64bf145045193/rpds_py-0.23.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5fb89edee2fa237584e532fbf78f0ddd1e49a47c7c8cfa153ab4849dc72a35e6", size = 557418 }, + { url = "https://files.pythonhosted.org/packages/9c/0a/68cf7228895b1a3f6f39f51b15830e62456795e61193d2c8b87fd48c60db/rpds_py-0.23.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:7e5413d2e2d86025e73f05510ad23dad5950ab8417b7fc6beaad99be8077138b", size = 583033 }, + { url = "https://files.pythonhosted.org/packages/14/18/017ab41dcd6649ad5db7d00155b4c212b31ab05bd857d5ba73a1617984eb/rpds_py-0.23.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:d31ed4987d72aabdf521eddfb6a72988703c091cfc0064330b9e5f8d6a042ff5", size = 554880 }, + { url = "https://files.pythonhosted.org/packages/2e/dd/17de89431268da8819d8d51ce67beac28d9b22fccf437bc5d6d2bcd1acdb/rpds_py-0.23.1-cp313-cp313t-win32.whl", hash = "sha256:f3429fb8e15b20961efca8c8b21432623d85db2228cc73fe22756c6637aa39e7", size = 219743 }, + { url = "https://files.pythonhosted.org/packages/68/15/6d22d07e063ce5e9bfbd96db9ec2fbb4693591b4503e3a76996639474d02/rpds_py-0.23.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d6f6512a90bd5cd9030a6237f5346f046c6f0e40af98657568fa45695d4de59d", size = 235415 }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 }, +] + +[[package]] +name = "smmap" +version = "5.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/44/cd/a040c4b3119bbe532e5b0732286f805445375489fceaec1f48306068ee3b/smmap-5.0.2.tar.gz", hash = "sha256:26ea65a03958fa0c8a1c7e8c7a58fdc77221b8910f6be2131affade476898ad5", size = 22329 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/be/d09147ad1ec7934636ad912901c5fd7667e1c858e19d355237db0d0cd5e4/smmap-5.0.2-py3-none-any.whl", hash = "sha256:b30115f0def7d7531d22a0fb6502488d879e75b260a9db4d0819cfb25403af5e", size = 24303 }, +] + +[[package]] +name = "sniffio" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 }, +] + +[[package]] +name = "streamlit" +version = "1.43.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "altair" }, + { name = "blinker" }, + { name = "cachetools" }, + { name = "click" }, + { name = "gitpython" }, + { name = "numpy" }, + { name = "packaging" }, + { name = "pandas" }, + { name = "pillow" }, + { name = "protobuf" }, + { name = "pyarrow" }, + { name = "pydeck" }, + { name = "requests" }, + { name = "tenacity" }, + { name = "toml" }, + { name = "tornado" }, + { name = "typing-extensions" }, + { name = "watchdog", marker = "sys_platform != 'darwin'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/48/a6/4806b73a5296961f7654a3426496bd463f2b92f6e1b75c359e29317e7a15/streamlit-1.43.1.tar.gz", hash = "sha256:4bb1e1235f6d84c279da567bf314f6e12310d53f1fb2350176ee462e189baebb", size = 9344698 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bf/14/a95ac354fe9ca59ee9d030dc738dac8ac04bd32f9d7c3f2c790eb6431ab1/streamlit-1.43.1-py2.py3-none-any.whl", hash = "sha256:ed8a6bd01e2b087e25b1b534835a341c2f71964822ac94b065c5496d2b5b49c2", size = 9734547 }, +] + +[[package]] +name = "tenacity" +version = "9.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/94/91fccdb4b8110642462e653d5dcb27e7b674742ad68efd146367da7bdb10/tenacity-9.0.0.tar.gz", hash = "sha256:807f37ca97d62aa361264d497b0e31e92b8027044942bfa756160d908320d73b", size = 47421 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/cb/b86984bed139586d01532a587464b5805f12e397594f19f931c4c2fbfa61/tenacity-9.0.0-py3-none-any.whl", hash = "sha256:93de0c98785b27fcf659856aa9f54bfbd399e29969b0621bc7f762bd441b4539", size = 28169 }, +] + +[[package]] +name = "toml" +version = "0.10.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/be/ba/1f744cdc819428fc6b5084ec34d9b30660f6f9daaf70eead706e3203ec3c/toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f", size = 22253 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/44/6f/7120676b6d73228c96e17f1f794d8ab046fc910d781c8d151120c3f1569e/toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", size = 16588 }, +] + +[[package]] +name = "tornado" +version = "6.4.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/59/45/a0daf161f7d6f36c3ea5fc0c2de619746cc3dd4c76402e9db545bd920f63/tornado-6.4.2.tar.gz", hash = "sha256:92bad5b4746e9879fd7bf1eb21dce4e3fc5128d71601f80005afa39237ad620b", size = 501135 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/7e/71f604d8cea1b58f82ba3590290b66da1e72d840aeb37e0d5f7291bd30db/tornado-6.4.2-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e828cce1123e9e44ae2a50a9de3055497ab1d0aeb440c5ac23064d9e44880da1", size = 436299 }, + { url = "https://files.pythonhosted.org/packages/96/44/87543a3b99016d0bf54fdaab30d24bf0af2e848f1d13d34a3a5380aabe16/tornado-6.4.2-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:072ce12ada169c5b00b7d92a99ba089447ccc993ea2143c9ede887e0937aa803", size = 434253 }, + { url = "https://files.pythonhosted.org/packages/cb/fb/fdf679b4ce51bcb7210801ef4f11fdac96e9885daa402861751353beea6e/tornado-6.4.2-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a017d239bd1bb0919f72af256a970624241f070496635784d9bf0db640d3fec", size = 437602 }, + { url = "https://files.pythonhosted.org/packages/4f/3b/e31aeffffc22b475a64dbeb273026a21b5b566f74dee48742817626c47dc/tornado-6.4.2-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c36e62ce8f63409301537222faffcef7dfc5284f27eec227389f2ad11b09d946", size = 436972 }, + { url = "https://files.pythonhosted.org/packages/22/55/b78a464de78051a30599ceb6983b01d8f732e6f69bf37b4ed07f642ac0fc/tornado-6.4.2-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca9eb02196e789c9cb5c3c7c0f04fb447dc2adffd95265b2c7223a8a615ccbf", size = 437173 }, + { url = "https://files.pythonhosted.org/packages/79/5e/be4fb0d1684eb822c9a62fb18a3e44a06188f78aa466b2ad991d2ee31104/tornado-6.4.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:304463bd0772442ff4d0f5149c6f1c2135a1fae045adf070821c6cdc76980634", size = 437892 }, + { url = "https://files.pythonhosted.org/packages/f5/33/4f91fdd94ea36e1d796147003b490fe60a0215ac5737b6f9c65e160d4fe0/tornado-6.4.2-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:c82c46813ba483a385ab2a99caeaedf92585a1f90defb5693351fa7e4ea0bf73", size = 437334 }, + { url = "https://files.pythonhosted.org/packages/2b/ae/c1b22d4524b0e10da2f29a176fb2890386f7bd1f63aacf186444873a88a0/tornado-6.4.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:932d195ca9015956fa502c6b56af9eb06106140d844a335590c1ec7f5277d10c", size = 437261 }, + { url = "https://files.pythonhosted.org/packages/b5/25/36dbd49ab6d179bcfc4c6c093a51795a4f3bed380543a8242ac3517a1751/tornado-6.4.2-cp38-abi3-win32.whl", hash = "sha256:2876cef82e6c5978fde1e0d5b1f919d756968d5b4282418f3146b79b58556482", size = 438463 }, + { url = "https://files.pythonhosted.org/packages/61/cc/58b1adeb1bb46228442081e746fcdbc4540905c87e8add7c277540934edb/tornado-6.4.2-cp38-abi3-win_amd64.whl", hash = "sha256:908b71bf3ff37d81073356a5fadcc660eb10c1476ee6e2725588626ce7e5ca38", size = 438907 }, +] + +[[package]] +name = "tqdm" +version = "4.67.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'win32'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d0/30/dc54f88dd4a2b5dc8a0279bdd7270e735851848b762aeb1c1184ed1f6b14/tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", size = 78540 }, +] + +[[package]] +name = "typing-extensions" +version = "4.12.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/df/db/f35a00659bc03fec321ba8bce9420de607a1d37f8342eee1863174c69557/typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8", size = 85321 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/26/9f/ad63fc0248c5379346306f8668cda6e2e2e9c95e01216d2b8ffd9ff037d0/typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", size = 37438 }, +] + +[[package]] +name = "tzdata" +version = "2025.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/0f/fa4723f22942480be4ca9527bbde8d43f6c3f2fe8412f00e7f5f6746bc8b/tzdata-2025.1.tar.gz", hash = "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694", size = 194950 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/dd/84f10e23edd882c6f968c21c2434fe67bd4a528967067515feca9e611e5e/tzdata-2025.1-py2.py3-none-any.whl", hash = "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639", size = 346762 }, +] + +[[package]] +name = "urllib3" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/63/e53da845320b757bf29ef6a9062f5c669fe997973f966045cb019c3f4b66/urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d", size = 307268 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c8/19/4ec628951a74043532ca2cf5d97b7b14863931476d117c471e8e2b1eb39f/urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", size = 128369 }, +] + +[[package]] +name = "watchdog" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079 }, + { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076 }, + { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077 }, + { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077 }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078 }, + { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065 }, + { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070 }, + { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067 }, +] + +[[package]] +name = "wrapt" +version = "1.17.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/fc/e91cc220803d7bc4db93fb02facd8461c37364151b8494762cc88b0fbcef/wrapt-1.17.2.tar.gz", hash = "sha256:41388e9d4d1522446fe79d3213196bd9e3b301a336965b9e27ca2788ebd122f3", size = 55531 } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a1/bd/ab55f849fd1f9a58ed7ea47f5559ff09741b25f00c191231f9f059c83949/wrapt-1.17.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d5e2439eecc762cd85e7bd37161d4714aa03a33c5ba884e26c81559817ca0925", size = 53799 }, + { url = "https://files.pythonhosted.org/packages/53/18/75ddc64c3f63988f5a1d7e10fb204ffe5762bc663f8023f18ecaf31a332e/wrapt-1.17.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fc7cb4c1c744f8c05cd5f9438a3caa6ab94ce8344e952d7c45a8ed59dd88392", size = 38821 }, + { url = "https://files.pythonhosted.org/packages/48/2a/97928387d6ed1c1ebbfd4efc4133a0633546bec8481a2dd5ec961313a1c7/wrapt-1.17.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8fdbdb757d5390f7c675e558fd3186d590973244fab0c5fe63d373ade3e99d40", size = 38919 }, + { url = "https://files.pythonhosted.org/packages/73/54/3bfe5a1febbbccb7a2f77de47b989c0b85ed3a6a41614b104204a788c20e/wrapt-1.17.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bb1d0dbf99411f3d871deb6faa9aabb9d4e744d67dcaaa05399af89d847a91d", size = 88721 }, + { url = "https://files.pythonhosted.org/packages/25/cb/7262bc1b0300b4b64af50c2720ef958c2c1917525238d661c3e9a2b71b7b/wrapt-1.17.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d18a4865f46b8579d44e4fe1e2bcbc6472ad83d98e22a26c963d46e4c125ef0b", size = 80899 }, + { url = "https://files.pythonhosted.org/packages/2a/5a/04cde32b07a7431d4ed0553a76fdb7a61270e78c5fd5a603e190ac389f14/wrapt-1.17.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc570b5f14a79734437cb7b0500376b6b791153314986074486e0b0fa8d71d98", size = 89222 }, + { url = "https://files.pythonhosted.org/packages/09/28/2e45a4f4771fcfb109e244d5dbe54259e970362a311b67a965555ba65026/wrapt-1.17.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6d9187b01bebc3875bac9b087948a2bccefe464a7d8f627cf6e48b1bbae30f82", size = 86707 }, + { url = "https://files.pythonhosted.org/packages/c6/d2/dcb56bf5f32fcd4bd9aacc77b50a539abdd5b6536872413fd3f428b21bed/wrapt-1.17.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9e8659775f1adf02eb1e6f109751268e493c73716ca5761f8acb695e52a756ae", size = 79685 }, + { url = "https://files.pythonhosted.org/packages/80/4e/eb8b353e36711347893f502ce91c770b0b0929f8f0bed2670a6856e667a9/wrapt-1.17.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e8b2816ebef96d83657b56306152a93909a83f23994f4b30ad4573b00bd11bb9", size = 87567 }, + { url = "https://files.pythonhosted.org/packages/17/27/4fe749a54e7fae6e7146f1c7d914d28ef599dacd4416566c055564080fe2/wrapt-1.17.2-cp312-cp312-win32.whl", hash = "sha256:468090021f391fe0056ad3e807e3d9034e0fd01adcd3bdfba977b6fdf4213ea9", size = 36672 }, + { url = "https://files.pythonhosted.org/packages/15/06/1dbf478ea45c03e78a6a8c4be4fdc3c3bddea5c8de8a93bc971415e47f0f/wrapt-1.17.2-cp312-cp312-win_amd64.whl", hash = "sha256:ec89ed91f2fa8e3f52ae53cd3cf640d6feff92ba90d62236a81e4e563ac0e991", size = 38865 }, + { url = "https://files.pythonhosted.org/packages/ce/b9/0ffd557a92f3b11d4c5d5e0c5e4ad057bd9eb8586615cdaf901409920b14/wrapt-1.17.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6ed6ffac43aecfe6d86ec5b74b06a5be33d5bb9243d055141e8cabb12aa08125", size = 53800 }, + { url = "https://files.pythonhosted.org/packages/c0/ef/8be90a0b7e73c32e550c73cfb2fa09db62234227ece47b0e80a05073b375/wrapt-1.17.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35621ae4c00e056adb0009f8e86e28eb4a41a4bfa8f9bfa9fca7d343fe94f998", size = 38824 }, + { url = "https://files.pythonhosted.org/packages/36/89/0aae34c10fe524cce30fe5fc433210376bce94cf74d05b0d68344c8ba46e/wrapt-1.17.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a604bf7a053f8362d27eb9fefd2097f82600b856d5abe996d623babd067b1ab5", size = 38920 }, + { url = "https://files.pythonhosted.org/packages/3b/24/11c4510de906d77e0cfb5197f1b1445d4fec42c9a39ea853d482698ac681/wrapt-1.17.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cbabee4f083b6b4cd282f5b817a867cf0b1028c54d445b7ec7cfe6505057cf8", size = 88690 }, + { url = "https://files.pythonhosted.org/packages/71/d7/cfcf842291267bf455b3e266c0c29dcb675b5540ee8b50ba1699abf3af45/wrapt-1.17.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49703ce2ddc220df165bd2962f8e03b84c89fee2d65e1c24a7defff6f988f4d6", size = 80861 }, + { url = "https://files.pythonhosted.org/packages/d5/66/5d973e9f3e7370fd686fb47a9af3319418ed925c27d72ce16b791231576d/wrapt-1.17.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112e52c5822fc4253f3901b676c55ddf288614dc7011634e2719718eaa187dc", size = 89174 }, + { url = "https://files.pythonhosted.org/packages/a7/d3/8e17bb70f6ae25dabc1aaf990f86824e4fd98ee9cadf197054e068500d27/wrapt-1.17.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:9fee687dce376205d9a494e9c121e27183b2a3df18037f89d69bd7b35bcf59e2", size = 86721 }, + { url = "https://files.pythonhosted.org/packages/6f/54/f170dfb278fe1c30d0ff864513cff526d624ab8de3254b20abb9cffedc24/wrapt-1.17.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:18983c537e04d11cf027fbb60a1e8dfd5190e2b60cc27bc0808e653e7b218d1b", size = 79763 }, + { url = "https://files.pythonhosted.org/packages/4a/98/de07243751f1c4a9b15c76019250210dd3486ce098c3d80d5f729cba029c/wrapt-1.17.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:703919b1633412ab54bcf920ab388735832fdcb9f9a00ae49387f0fe67dad504", size = 87585 }, + { url = "https://files.pythonhosted.org/packages/f9/f0/13925f4bd6548013038cdeb11ee2cbd4e37c30f8bfd5db9e5a2a370d6e20/wrapt-1.17.2-cp313-cp313-win32.whl", hash = "sha256:abbb9e76177c35d4e8568e58650aa6926040d6a9f6f03435b7a522bf1c487f9a", size = 36676 }, + { url = "https://files.pythonhosted.org/packages/bf/ae/743f16ef8c2e3628df3ddfd652b7d4c555d12c84b53f3d8218498f4ade9b/wrapt-1.17.2-cp313-cp313-win_amd64.whl", hash = "sha256:69606d7bb691b50a4240ce6b22ebb319c1cfb164e5f6569835058196e0f3a845", size = 38871 }, + { url = "https://files.pythonhosted.org/packages/3d/bc/30f903f891a82d402ffb5fda27ec1d621cc97cb74c16fea0b6141f1d4e87/wrapt-1.17.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:4a721d3c943dae44f8e243b380cb645a709ba5bd35d3ad27bc2ed947e9c68192", size = 56312 }, + { url = "https://files.pythonhosted.org/packages/8a/04/c97273eb491b5f1c918857cd26f314b74fc9b29224521f5b83f872253725/wrapt-1.17.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:766d8bbefcb9e00c3ac3b000d9acc51f1b399513f44d77dfe0eb026ad7c9a19b", size = 40062 }, + { url = "https://files.pythonhosted.org/packages/4e/ca/3b7afa1eae3a9e7fefe499db9b96813f41828b9fdb016ee836c4c379dadb/wrapt-1.17.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e496a8ce2c256da1eb98bd15803a79bee00fc351f5dfb9ea82594a3f058309e0", size = 40155 }, + { url = "https://files.pythonhosted.org/packages/89/be/7c1baed43290775cb9030c774bc53c860db140397047cc49aedaf0a15477/wrapt-1.17.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40d615e4fe22f4ad3528448c193b218e077656ca9ccb22ce2cb20db730f8d306", size = 113471 }, + { url = "https://files.pythonhosted.org/packages/32/98/4ed894cf012b6d6aae5f5cc974006bdeb92f0241775addad3f8cd6ab71c8/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a5aaeff38654462bc4b09023918b7f21790efb807f54c000a39d41d69cf552cb", size = 101208 }, + { url = "https://files.pythonhosted.org/packages/ea/fd/0c30f2301ca94e655e5e057012e83284ce8c545df7661a78d8bfca2fac7a/wrapt-1.17.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a7d15bbd2bc99e92e39f49a04653062ee6085c0e18b3b7512a4f2fe91f2d681", size = 109339 }, + { url = "https://files.pythonhosted.org/packages/75/56/05d000de894c4cfcb84bcd6b1df6214297b8089a7bd324c21a4765e49b14/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e3890b508a23299083e065f435a492b5435eba6e304a7114d2f919d400888cc6", size = 110232 }, + { url = "https://files.pythonhosted.org/packages/53/f8/c3f6b2cf9b9277fb0813418e1503e68414cd036b3b099c823379c9575e6d/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:8c8b293cd65ad716d13d8dd3624e42e5a19cc2a2f1acc74b30c2c13f15cb61a6", size = 100476 }, + { url = "https://files.pythonhosted.org/packages/a7/b1/0bb11e29aa5139d90b770ebbfa167267b1fc548d2302c30c8f7572851738/wrapt-1.17.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4c82b8785d98cdd9fed4cac84d765d234ed3251bd6afe34cb7ac523cb93e8b4f", size = 106377 }, + { url = "https://files.pythonhosted.org/packages/6a/e1/0122853035b40b3f333bbb25f1939fc1045e21dd518f7f0922b60c156f7c/wrapt-1.17.2-cp313-cp313t-win32.whl", hash = "sha256:13e6afb7fe71fe7485a4550a8844cc9ffbe263c0f1a1eea569bc7091d4898555", size = 37986 }, + { url = "https://files.pythonhosted.org/packages/09/5e/1655cf481e079c1f22d0cabdd4e51733679932718dc23bf2db175f329b76/wrapt-1.17.2-cp313-cp313t-win_amd64.whl", hash = "sha256:eaf675418ed6b3b31c7a989fd007fa7c3be66ce14e5c3b27336383604c9da85c", size = 40750 }, + { url = "https://files.pythonhosted.org/packages/2d/82/f56956041adef78f849db6b289b282e72b55ab8045a75abad81898c28d19/wrapt-1.17.2-py3-none-any.whl", hash = "sha256:b18f2d1533a71f069c7f82d524a52599053d4c7166e9dd374ae2136b7f40f7c8", size = 23594 }, +]