diff --git a/venv/lib/python3.13/site-packages/certifi-2025.11.12.dist-info/licenses/LICENSE b/venv/lib/python3.13/site-packages/certifi-2025.11.12.dist-info/licenses/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..62b076cdee58ec8f34034141ba0befd9015b0c7e --- /dev/null +++ b/venv/lib/python3.13/site-packages/certifi-2025.11.12.dist-info/licenses/LICENSE @@ -0,0 +1,20 @@ +This package contains a modified version of ca-bundle.crt: + +ca-bundle.crt -- Bundle of CA Root Certificates + +This is a bundle of X.509 certificates of public Certificate Authorities +(CA). These were automatically extracted from Mozilla's root certificates +file (certdata.txt). This file can be found in the mozilla source tree: +https://hg.mozilla.org/mozilla-central/file/tip/security/nss/lib/ckfw/builtins/certdata.txt +It contains the certificates in PEM format and therefore +can be directly used with curl / libcurl / php_curl, or with +an Apache+mod_ssl webserver for SSL client authentication. +Just configure this file as the SSLCACertificateFile.# + +***** BEGIN LICENSE BLOCK ***** +This Source Code Form is subject to the terms of the Mozilla Public License, +v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain +one at http://mozilla.org/MPL/2.0/. + +***** END LICENSE BLOCK ***** +@(#) $RCSfile: certdata.txt,v $ $Revision: 1.80 $ $Date: 2011/11/03 15:11:58 $ diff --git a/venv/lib/python3.13/site-packages/certifi/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/certifi/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ae75b743109ecf663b9ceeb54a36c41da04c5ade Binary files /dev/null and b/venv/lib/python3.13/site-packages/certifi/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/certifi/__pycache__/__main__.cpython-313.pyc b/venv/lib/python3.13/site-packages/certifi/__pycache__/__main__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7fee7b66750a2fbe41e653ebf0c8ccbf1ea69c43 Binary files /dev/null and b/venv/lib/python3.13/site-packages/certifi/__pycache__/__main__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/certifi/__pycache__/core.cpython-313.pyc b/venv/lib/python3.13/site-packages/certifi/__pycache__/core.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0026327b85ada4ba94447b31fb2dc2cf857f2150 Binary files /dev/null and b/venv/lib/python3.13/site-packages/certifi/__pycache__/core.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/charset_normalizer/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/charset_normalizer/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f67dc80dfa9fe39c4c766567be67e7ad8f1ae911 Binary files /dev/null and b/venv/lib/python3.13/site-packages/charset_normalizer/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/charset_normalizer/__pycache__/__main__.cpython-313.pyc b/venv/lib/python3.13/site-packages/charset_normalizer/__pycache__/__main__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9962ddcaaa6374d2dd67804b1cb2dd3e844480a6 Binary files /dev/null and b/venv/lib/python3.13/site-packages/charset_normalizer/__pycache__/__main__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/charset_normalizer/__pycache__/api.cpython-313.pyc b/venv/lib/python3.13/site-packages/charset_normalizer/__pycache__/api.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d06ec27d3d2f33a02f47f52f6cce98ad14c078c8 Binary files /dev/null and b/venv/lib/python3.13/site-packages/charset_normalizer/__pycache__/api.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/charset_normalizer/__pycache__/cd.cpython-313.pyc b/venv/lib/python3.13/site-packages/charset_normalizer/__pycache__/cd.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e3ecc1f0c644e24c33a7758054945eda7a500eba Binary files /dev/null and b/venv/lib/python3.13/site-packages/charset_normalizer/__pycache__/cd.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/charset_normalizer/__pycache__/constant.cpython-313.pyc b/venv/lib/python3.13/site-packages/charset_normalizer/__pycache__/constant.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..54241b4098a64f6ec59b6275533cf1f03f450172 Binary files /dev/null and b/venv/lib/python3.13/site-packages/charset_normalizer/__pycache__/constant.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/charset_normalizer/__pycache__/legacy.cpython-313.pyc b/venv/lib/python3.13/site-packages/charset_normalizer/__pycache__/legacy.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..21fa767305c9c52e005b35d6b7766c0dc43c3102 Binary files /dev/null and b/venv/lib/python3.13/site-packages/charset_normalizer/__pycache__/legacy.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/charset_normalizer/__pycache__/md.cpython-313.pyc b/venv/lib/python3.13/site-packages/charset_normalizer/__pycache__/md.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..db827e35ce6093c53079c51c020da5b01dddef48 Binary files /dev/null and b/venv/lib/python3.13/site-packages/charset_normalizer/__pycache__/md.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/charset_normalizer/__pycache__/models.cpython-313.pyc b/venv/lib/python3.13/site-packages/charset_normalizer/__pycache__/models.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..052fc85324b792de39bb32dad2709c4e909b9b8a Binary files /dev/null and b/venv/lib/python3.13/site-packages/charset_normalizer/__pycache__/models.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/charset_normalizer/__pycache__/utils.cpython-313.pyc b/venv/lib/python3.13/site-packages/charset_normalizer/__pycache__/utils.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..406d2a3544a063c1c411f5a3d0f60231dfb0f71f Binary files /dev/null and b/venv/lib/python3.13/site-packages/charset_normalizer/__pycache__/utils.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/charset_normalizer/__pycache__/version.cpython-313.pyc b/venv/lib/python3.13/site-packages/charset_normalizer/__pycache__/version.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..50eda49ebe321725c4361b0cb65b0de04564031c Binary files /dev/null and b/venv/lib/python3.13/site-packages/charset_normalizer/__pycache__/version.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/charset_normalizer/cli/__init__.py b/venv/lib/python3.13/site-packages/charset_normalizer/cli/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..543a5a4de49d07690e73df778aa580589d0789c6 --- /dev/null +++ b/venv/lib/python3.13/site-packages/charset_normalizer/cli/__init__.py @@ -0,0 +1,8 @@ +from __future__ import annotations + +from .__main__ import cli_detect, query_yes_no + +__all__ = ( + "cli_detect", + "query_yes_no", +) diff --git a/venv/lib/python3.13/site-packages/charset_normalizer/cli/__main__.py b/venv/lib/python3.13/site-packages/charset_normalizer/cli/__main__.py new file mode 100644 index 0000000000000000000000000000000000000000..cb64156a0fc164442acc4f4517975a5699d26354 --- /dev/null +++ b/venv/lib/python3.13/site-packages/charset_normalizer/cli/__main__.py @@ -0,0 +1,381 @@ +from __future__ import annotations + +import argparse +import sys +import typing +from json import dumps +from os.path import abspath, basename, dirname, join, realpath +from platform import python_version +from unicodedata import unidata_version + +import charset_normalizer.md as md_module +from charset_normalizer import from_fp +from charset_normalizer.models import CliDetectionResult +from charset_normalizer.version import __version__ + + +def query_yes_no(question: str, default: str = "yes") -> bool: + """Ask a yes/no question via input() and return their answer. + + "question" is a string that is presented to the user. + "default" is the presumed answer if the user just hits . + It must be "yes" (the default), "no" or None (meaning + an answer is required of the user). + + The "answer" return value is True for "yes" or False for "no". + + Credit goes to (c) https://stackoverflow.com/questions/3041986/apt-command-line-interface-like-yes-no-input + """ + valid = {"yes": True, "y": True, "ye": True, "no": False, "n": False} + if default is None: + prompt = " [y/n] " + elif default == "yes": + prompt = " [Y/n] " + elif default == "no": + prompt = " [y/N] " + else: + raise ValueError("invalid default answer: '%s'" % default) + + while True: + sys.stdout.write(question + prompt) + choice = input().lower() + if default is not None and choice == "": + return valid[default] + elif choice in valid: + return valid[choice] + else: + sys.stdout.write("Please respond with 'yes' or 'no' (or 'y' or 'n').\n") + + +class FileType: + """Factory for creating file object types + + Instances of FileType are typically passed as type= arguments to the + ArgumentParser add_argument() method. + + Keyword Arguments: + - mode -- A string indicating how the file is to be opened. Accepts the + same values as the builtin open() function. + - bufsize -- The file's desired buffer size. Accepts the same values as + the builtin open() function. + - encoding -- The file's encoding. Accepts the same values as the + builtin open() function. + - errors -- A string indicating how encoding and decoding errors are to + be handled. Accepts the same value as the builtin open() function. + + Backported from CPython 3.12 + """ + + def __init__( + self, + mode: str = "r", + bufsize: int = -1, + encoding: str | None = None, + errors: str | None = None, + ): + self._mode = mode + self._bufsize = bufsize + self._encoding = encoding + self._errors = errors + + def __call__(self, string: str) -> typing.IO: # type: ignore[type-arg] + # the special argument "-" means sys.std{in,out} + if string == "-": + if "r" in self._mode: + return sys.stdin.buffer if "b" in self._mode else sys.stdin + elif any(c in self._mode for c in "wax"): + return sys.stdout.buffer if "b" in self._mode else sys.stdout + else: + msg = f'argument "-" with mode {self._mode}' + raise ValueError(msg) + + # all other arguments are used as file names + try: + return open(string, self._mode, self._bufsize, self._encoding, self._errors) + except OSError as e: + message = f"can't open '{string}': {e}" + raise argparse.ArgumentTypeError(message) + + def __repr__(self) -> str: + args = self._mode, self._bufsize + kwargs = [("encoding", self._encoding), ("errors", self._errors)] + args_str = ", ".join( + [repr(arg) for arg in args if arg != -1] + + [f"{kw}={arg!r}" for kw, arg in kwargs if arg is not None] + ) + return f"{type(self).__name__}({args_str})" + + +def cli_detect(argv: list[str] | None = None) -> int: + """ + CLI assistant using ARGV and ArgumentParser + :param argv: + :return: 0 if everything is fine, anything else equal trouble + """ + parser = argparse.ArgumentParser( + description="The Real First Universal Charset Detector. " + "Discover originating encoding used on text file. " + "Normalize text to unicode." + ) + + parser.add_argument( + "files", type=FileType("rb"), nargs="+", help="File(s) to be analysed" + ) + parser.add_argument( + "-v", + "--verbose", + action="store_true", + default=False, + dest="verbose", + help="Display complementary information about file if any. " + "Stdout will contain logs about the detection process.", + ) + parser.add_argument( + "-a", + "--with-alternative", + action="store_true", + default=False, + dest="alternatives", + help="Output complementary possibilities if any. Top-level JSON WILL be a list.", + ) + parser.add_argument( + "-n", + "--normalize", + action="store_true", + default=False, + dest="normalize", + help="Permit to normalize input file. If not set, program does not write anything.", + ) + parser.add_argument( + "-m", + "--minimal", + action="store_true", + default=False, + dest="minimal", + help="Only output the charset detected to STDOUT. Disabling JSON output.", + ) + parser.add_argument( + "-r", + "--replace", + action="store_true", + default=False, + dest="replace", + help="Replace file when trying to normalize it instead of creating a new one.", + ) + parser.add_argument( + "-f", + "--force", + action="store_true", + default=False, + dest="force", + help="Replace file without asking if you are sure, use this flag with caution.", + ) + parser.add_argument( + "-i", + "--no-preemptive", + action="store_true", + default=False, + dest="no_preemptive", + help="Disable looking at a charset declaration to hint the detector.", + ) + parser.add_argument( + "-t", + "--threshold", + action="store", + default=0.2, + type=float, + dest="threshold", + help="Define a custom maximum amount of noise allowed in decoded content. 0. <= noise <= 1.", + ) + parser.add_argument( + "--version", + action="version", + version="Charset-Normalizer {} - Python {} - Unicode {} - SpeedUp {}".format( + __version__, + python_version(), + unidata_version, + "OFF" if md_module.__file__.lower().endswith(".py") else "ON", + ), + help="Show version information and exit.", + ) + + args = parser.parse_args(argv) + + if args.replace is True and args.normalize is False: + if args.files: + for my_file in args.files: + my_file.close() + print("Use --replace in addition of --normalize only.", file=sys.stderr) + return 1 + + if args.force is True and args.replace is False: + if args.files: + for my_file in args.files: + my_file.close() + print("Use --force in addition of --replace only.", file=sys.stderr) + return 1 + + if args.threshold < 0.0 or args.threshold > 1.0: + if args.files: + for my_file in args.files: + my_file.close() + print("--threshold VALUE should be between 0. AND 1.", file=sys.stderr) + return 1 + + x_ = [] + + for my_file in args.files: + matches = from_fp( + my_file, + threshold=args.threshold, + explain=args.verbose, + preemptive_behaviour=args.no_preemptive is False, + ) + + best_guess = matches.best() + + if best_guess is None: + print( + 'Unable to identify originating encoding for "{}". {}'.format( + my_file.name, + ( + "Maybe try increasing maximum amount of chaos." + if args.threshold < 1.0 + else "" + ), + ), + file=sys.stderr, + ) + x_.append( + CliDetectionResult( + abspath(my_file.name), + None, + [], + [], + "Unknown", + [], + False, + 1.0, + 0.0, + None, + True, + ) + ) + else: + x_.append( + CliDetectionResult( + abspath(my_file.name), + best_guess.encoding, + best_guess.encoding_aliases, + [ + cp + for cp in best_guess.could_be_from_charset + if cp != best_guess.encoding + ], + best_guess.language, + best_guess.alphabets, + best_guess.bom, + best_guess.percent_chaos, + best_guess.percent_coherence, + None, + True, + ) + ) + + if len(matches) > 1 and args.alternatives: + for el in matches: + if el != best_guess: + x_.append( + CliDetectionResult( + abspath(my_file.name), + el.encoding, + el.encoding_aliases, + [ + cp + for cp in el.could_be_from_charset + if cp != el.encoding + ], + el.language, + el.alphabets, + el.bom, + el.percent_chaos, + el.percent_coherence, + None, + False, + ) + ) + + if args.normalize is True: + if best_guess.encoding.startswith("utf") is True: + print( + '"{}" file does not need to be normalized, as it already came from unicode.'.format( + my_file.name + ), + file=sys.stderr, + ) + if my_file.closed is False: + my_file.close() + continue + + dir_path = dirname(realpath(my_file.name)) + file_name = basename(realpath(my_file.name)) + + o_: list[str] = file_name.split(".") + + if args.replace is False: + o_.insert(-1, best_guess.encoding) + if my_file.closed is False: + my_file.close() + elif ( + args.force is False + and query_yes_no( + 'Are you sure to normalize "{}" by replacing it ?'.format( + my_file.name + ), + "no", + ) + is False + ): + if my_file.closed is False: + my_file.close() + continue + + try: + x_[0].unicode_path = join(dir_path, ".".join(o_)) + + with open(x_[0].unicode_path, "wb") as fp: + fp.write(best_guess.output()) + except OSError as e: + print(str(e), file=sys.stderr) + if my_file.closed is False: + my_file.close() + return 2 + + if my_file.closed is False: + my_file.close() + + if args.minimal is False: + print( + dumps( + [el.__dict__ for el in x_] if len(x_) > 1 else x_[0].__dict__, + ensure_ascii=True, + indent=4, + ) + ) + else: + for my_file in args.files: + print( + ", ".join( + [ + el.encoding or "undefined" + for el in x_ + if el.path == abspath(my_file.name) + ] + ) + ) + + return 0 + + +if __name__ == "__main__": + cli_detect() diff --git a/venv/lib/python3.13/site-packages/charset_normalizer/cli/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/charset_normalizer/cli/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4fd3b96948387bdf106e143b8e78fe955312ec23 Binary files /dev/null and b/venv/lib/python3.13/site-packages/charset_normalizer/cli/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/charset_normalizer/cli/__pycache__/__main__.cpython-313.pyc b/venv/lib/python3.13/site-packages/charset_normalizer/cli/__pycache__/__main__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ef08f2a663f28ad3cd759da801667a72865c66f0 Binary files /dev/null and b/venv/lib/python3.13/site-packages/charset_normalizer/cli/__pycache__/__main__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/filelock-3.20.0.dist-info/licenses/LICENSE b/venv/lib/python3.13/site-packages/filelock-3.20.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..cf1ab25da0349f84a3fdd40032f0ce99db813b8b --- /dev/null +++ b/venv/lib/python3.13/site-packages/filelock-3.20.0.dist-info/licenses/LICENSE @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to diff --git a/venv/lib/python3.13/site-packages/hf_xet-1.2.0.dist-info/licenses/LICENSE b/venv/lib/python3.13/site-packages/hf_xet-1.2.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64 --- /dev/null +++ b/venv/lib/python3.13/site-packages/hf_xet-1.2.0.dist-info/licenses/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..60cd1ce8bb9615f1decec55334d2d56de059c170 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_commit_api.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_commit_api.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e4a26fc741c163428aa193dea51e45bb46271620 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_commit_api.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_commit_scheduler.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_commit_scheduler.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..20ec20d626783df202f800cab223bda988237eaf Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_commit_scheduler.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_inference_endpoints.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_inference_endpoints.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..23c1df7df087c376168a6465bc4e373822f909fc Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_inference_endpoints.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_jobs_api.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_jobs_api.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..46de9383be6b7854a175473c08906e9920daa548 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_jobs_api.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_local_folder.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_local_folder.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..93640a32e0e9d0bb716e0e8c799e27dbee36b616 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_local_folder.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_login.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_login.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e9014f7b06cca87a014dcd92bd6d2163926d2803 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_login.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_oauth.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_oauth.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5c2fa2095a7f0811026ee5482a258bf60c2a359e Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_oauth.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_snapshot_download.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_snapshot_download.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..37cfc29b5fa43f208bb91820a1f2366ecbd5f451 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_snapshot_download.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_space_api.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_space_api.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..29a6da63096cdfe48a757ce15d8af09ed2d88746 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_space_api.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_tensorboard_logger.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_tensorboard_logger.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fc528de71f1698c8b67eca29e181eb60de5f59d0 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_tensorboard_logger.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_upload_large_folder.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_upload_large_folder.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..629c19ed5085859bf6c7de0f4b5fd21f59a28152 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_upload_large_folder.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_webhooks_payload.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_webhooks_payload.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b10d2c000398387486fe57f6df8c4d4426fa58c8 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_webhooks_payload.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_webhooks_server.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_webhooks_server.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8fc4539c56117a0bcb8de57d86c1ece72a605549 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/_webhooks_server.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/community.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/community.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3b69e540eb286768184a83bb4c99b822b64fbe40 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/community.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/constants.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/constants.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cefcac3c55e53a076e58191a96903800857af975 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/constants.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/dataclasses.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/dataclasses.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4eb9ed22a525443be0788de6e7ec626e79d2d4c6 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/dataclasses.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/errors.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/errors.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0b812c43571ea4b18f34aeaad4e86bc6d2376db5 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/errors.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/fastai_utils.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/fastai_utils.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..19fffc1ba0e8a4233f4079620e164d1b07dcef0e Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/fastai_utils.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/file_download.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/file_download.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..694ba9124232c26ac51695ba4cc40116aa1491d9 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/file_download.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/hf_file_system.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/hf_file_system.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e68f4326bf68fe44706ad5ce7f7e59427bfd0533 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/hf_file_system.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/hub_mixin.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/hub_mixin.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..24064f039ee28982305cf2d6f30fa1a0502844c0 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/hub_mixin.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/inference_api.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/inference_api.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..241ab80990cef9862cb6d569fc158783e2b57f62 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/inference_api.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/keras_mixin.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/keras_mixin.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f438306fb09f0421a610e113aa2d234d71a598f1 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/keras_mixin.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/lfs.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/lfs.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d5373dca1875bdf7093fde40724554ff87b850f7 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/lfs.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/repocard.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/repocard.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2c851009959278bee667096bf7c4eb1eb3f88907 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/repocard.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/repocard_data.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/repocard_data.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..71557be0ee95d31533124ffc82328263913a844e Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/repocard_data.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/repository.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/repository.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7bcd6ef4a9165ca5708dd9c00cb9a638a2915be4 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/__pycache__/repository.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/cli/__init__.py b/venv/lib/python3.13/site-packages/huggingface_hub/cli/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7a1a8d793b89e16e5fa46ec5d420ec96fe1d72fe --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/cli/__init__.py @@ -0,0 +1,27 @@ +# Copyright 2025 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from abc import ABC, abstractmethod +from argparse import _SubParsersAction + + +class BaseHuggingfaceCLICommand(ABC): + @staticmethod + @abstractmethod + def register_subcommand(parser: _SubParsersAction): + raise NotImplementedError() + + @abstractmethod + def run(self): + raise NotImplementedError() diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/cli/_cli_utils.py b/venv/lib/python3.13/site-packages/huggingface_hub/cli/_cli_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..bd56ad6896db2a257323e022896940c0ba0d68d3 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/cli/_cli_utils.py @@ -0,0 +1,69 @@ +# Copyright 2022 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains a utility for good-looking prints.""" + +import os +from typing import List, Union + + +class ANSI: + """ + Helper for en.wikipedia.org/wiki/ANSI_escape_code + """ + + _bold = "\u001b[1m" + _gray = "\u001b[90m" + _red = "\u001b[31m" + _reset = "\u001b[0m" + _yellow = "\u001b[33m" + + @classmethod + def bold(cls, s: str) -> str: + return cls._format(s, cls._bold) + + @classmethod + def gray(cls, s: str) -> str: + return cls._format(s, cls._gray) + + @classmethod + def red(cls, s: str) -> str: + return cls._format(s, cls._bold + cls._red) + + @classmethod + def yellow(cls, s: str) -> str: + return cls._format(s, cls._yellow) + + @classmethod + def _format(cls, s: str, code: str) -> str: + if os.environ.get("NO_COLOR"): + # See https://no-color.org/ + return s + return f"{code}{s}{cls._reset}" + + +def tabulate(rows: List[List[Union[str, int]]], headers: List[str]) -> str: + """ + Inspired by: + + - stackoverflow.com/a/8356620/593036 + - stackoverflow.com/questions/9535954/printing-lists-as-tabular-data + """ + col_widths = [max(len(str(x)) for x in col) for col in zip(*rows, headers)] + row_format = ("{{:{}}} " * len(headers)).format(*col_widths) + lines = [] + lines.append(row_format.format(*headers)) + lines.append(row_format.format(*["-" * w for w in col_widths])) + for row in rows: + lines.append(row_format.format(*row)) + return "\n".join(lines) diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/cli/auth.py b/venv/lib/python3.13/site-packages/huggingface_hub/cli/auth.py new file mode 100644 index 0000000000000000000000000000000000000000..bbf475a4f8785152b992b116a69b4b16293688f3 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/cli/auth.py @@ -0,0 +1,213 @@ +# Copyright 2020 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains commands to authenticate to the Hugging Face Hub and interact with your repositories. + +Usage: + # login and save token locally. + hf auth login --token=hf_*** --add-to-git-credential + + # switch between tokens + hf auth switch + + # list all tokens + hf auth list + + # logout from all tokens + hf auth logout + + # check which account you are logged in as + hf auth whoami +""" + +from argparse import _SubParsersAction +from typing import List, Optional + +from requests.exceptions import HTTPError + +from huggingface_hub.commands import BaseHuggingfaceCLICommand +from huggingface_hub.constants import ENDPOINT +from huggingface_hub.hf_api import HfApi + +from .._login import auth_list, auth_switch, login, logout +from ..utils import get_stored_tokens, get_token, logging +from ._cli_utils import ANSI + + +logger = logging.get_logger(__name__) + +try: + from InquirerPy import inquirer + from InquirerPy.base.control import Choice + + _inquirer_py_available = True +except ImportError: + _inquirer_py_available = False + + +class AuthCommands(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction): + # Create the main 'auth' command + auth_parser = parser.add_parser("auth", help="Manage authentication (login, logout, etc.).") + auth_subparsers = auth_parser.add_subparsers(help="Authentication subcommands") + + # Show help if no subcommand is provided + auth_parser.set_defaults(func=lambda args: auth_parser.print_help()) + + # Add 'login' as a subcommand of 'auth' + login_parser = auth_subparsers.add_parser( + "login", help="Log in using a token from huggingface.co/settings/tokens" + ) + login_parser.add_argument( + "--token", + type=str, + help="Token generated from https://huggingface.co/settings/tokens", + ) + login_parser.add_argument( + "--add-to-git-credential", + action="store_true", + help="Optional: Save token to git credential helper.", + ) + login_parser.set_defaults(func=lambda args: AuthLogin(args)) + + # Add 'logout' as a subcommand of 'auth' + logout_parser = auth_subparsers.add_parser("logout", help="Log out") + logout_parser.add_argument( + "--token-name", + type=str, + help="Optional: Name of the access token to log out from.", + ) + logout_parser.set_defaults(func=lambda args: AuthLogout(args)) + + # Add 'whoami' as a subcommand of 'auth' + whoami_parser = auth_subparsers.add_parser( + "whoami", help="Find out which huggingface.co account you are logged in as." + ) + whoami_parser.set_defaults(func=lambda args: AuthWhoami(args)) + + # Existing subcommands + auth_switch_parser = auth_subparsers.add_parser("switch", help="Switch between access tokens") + auth_switch_parser.add_argument( + "--token-name", + type=str, + help="Optional: Name of the access token to switch to.", + ) + auth_switch_parser.add_argument( + "--add-to-git-credential", + action="store_true", + help="Optional: Save token to git credential helper.", + ) + auth_switch_parser.set_defaults(func=lambda args: AuthSwitch(args)) + + auth_list_parser = auth_subparsers.add_parser("list", help="List all stored access tokens") + auth_list_parser.set_defaults(func=lambda args: AuthList(args)) + + +class BaseAuthCommand: + def __init__(self, args): + self.args = args + self._api = HfApi() + + +class AuthLogin(BaseAuthCommand): + def run(self): + logging.set_verbosity_info() + login( + token=self.args.token, + add_to_git_credential=self.args.add_to_git_credential, + ) + + +class AuthLogout(BaseAuthCommand): + def run(self): + logging.set_verbosity_info() + logout(token_name=self.args.token_name) + + +class AuthSwitch(BaseAuthCommand): + def run(self): + logging.set_verbosity_info() + token_name = self.args.token_name + if token_name is None: + token_name = self._select_token_name() + + if token_name is None: + print("No token name provided. Aborting.") + exit() + auth_switch(token_name, add_to_git_credential=self.args.add_to_git_credential) + + def _select_token_name(self) -> Optional[str]: + token_names = list(get_stored_tokens().keys()) + + if not token_names: + logger.error("No stored tokens found. Please login first.") + return None + + if _inquirer_py_available: + return self._select_token_name_tui(token_names) + # if inquirer is not available, use a simpler terminal UI + print("Available stored tokens:") + for i, token_name in enumerate(token_names, 1): + print(f"{i}. {token_name}") + while True: + try: + choice = input("Enter the number of the token to switch to (or 'q' to quit): ") + if choice.lower() == "q": + return None + index = int(choice) - 1 + if 0 <= index < len(token_names): + return token_names[index] + else: + print("Invalid selection. Please try again.") + except ValueError: + print("Invalid input. Please enter a number or 'q' to quit.") + + def _select_token_name_tui(self, token_names: List[str]) -> Optional[str]: + choices = [Choice(token_name, name=token_name) for token_name in token_names] + try: + return inquirer.select( + message="Select a token to switch to:", + choices=choices, + default=None, + ).execute() + except KeyboardInterrupt: + logger.info("Token selection cancelled.") + return None + + +class AuthList(BaseAuthCommand): + def run(self): + logging.set_verbosity_info() + auth_list() + + +class AuthWhoami(BaseAuthCommand): + def run(self): + token = get_token() + if token is None: + print("Not logged in") + exit() + try: + info = self._api.whoami(token) + print(ANSI.bold("user: "), info["name"]) + orgs = [org["name"] for org in info["orgs"]] + if orgs: + print(ANSI.bold("orgs: "), ",".join(orgs)) + + if ENDPOINT != "https://huggingface.co": + print(f"Authenticated through private endpoint: {ENDPOINT}") + except HTTPError as e: + print(e) + print(ANSI.red(e.response.text)) + exit(1) diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/cli/cache.py b/venv/lib/python3.13/site-packages/huggingface_hub/cli/cache.py new file mode 100644 index 0000000000000000000000000000000000000000..cc36ef5efd2508bcc5e32b1fbe222bb55358777c --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/cli/cache.py @@ -0,0 +1,403 @@ +# coding=utf-8 +# Copyright 2025-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains the 'hf cache' command group with 'scan' and 'delete' subcommands.""" + +import os +import time +from argparse import Namespace, _SubParsersAction +from functools import wraps +from tempfile import mkstemp +from typing import Any, Callable, Iterable, List, Literal, Optional, Union + +from ..utils import CachedRepoInfo, CachedRevisionInfo, CacheNotFound, HFCacheInfo, scan_cache_dir +from . import BaseHuggingfaceCLICommand +from ._cli_utils import ANSI, tabulate + + +# --- DELETE helpers (from delete_cache.py) --- +try: + from InquirerPy import inquirer + from InquirerPy.base.control import Choice + from InquirerPy.separator import Separator + + _inquirer_py_available = True +except ImportError: + _inquirer_py_available = False + +SortingOption_T = Literal["alphabetical", "lastUpdated", "lastUsed", "size"] +_CANCEL_DELETION_STR = "CANCEL_DELETION" + + +def require_inquirer_py(fn: Callable) -> Callable: + @wraps(fn) + def _inner(*args, **kwargs): + if not _inquirer_py_available: + raise ImportError( + "The 'cache delete' command requires extra dependencies for the TUI.\n" + "Please run 'pip install \"huggingface_hub[cli]\"' to install them.\n" + "Otherwise, disable TUI using the '--disable-tui' flag." + ) + return fn(*args, **kwargs) + + return _inner + + +class CacheCommand(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction): + cache_parser = parser.add_parser("cache", help="Manage local cache directory.") + cache_subparsers = cache_parser.add_subparsers(dest="cache_command", help="Cache subcommands") + + # Show help if no subcommand is provided + cache_parser.set_defaults(func=lambda args: cache_parser.print_help()) + + # Scan subcommand + scan_parser = cache_subparsers.add_parser("scan", help="Scan cache directory.") + scan_parser.add_argument( + "--dir", + type=str, + default=None, + help="cache directory to scan (optional). Default to the default HuggingFace cache.", + ) + scan_parser.add_argument( + "-v", + "--verbose", + action="count", + default=0, + help="show a more verbose output", + ) + scan_parser.set_defaults(func=CacheCommand, cache_command="scan") + # Delete subcommand + delete_parser = cache_subparsers.add_parser("delete", help="Delete revisions from the cache directory.") + delete_parser.add_argument( + "--dir", + type=str, + default=None, + help="cache directory (optional). Default to the default HuggingFace cache.", + ) + delete_parser.add_argument( + "--disable-tui", + action="store_true", + help=( + "Disable Terminal User Interface (TUI) mode. Useful if your platform/terminal doesn't support the multiselect menu." + ), + ) + delete_parser.add_argument( + "--sort", + nargs="?", + choices=["alphabetical", "lastUpdated", "lastUsed", "size"], + help=( + "Sort repositories by the specified criteria. Options: " + "'alphabetical' (A-Z), " + "'lastUpdated' (newest first), " + "'lastUsed' (most recent first), " + "'size' (largest first)." + ), + ) + delete_parser.set_defaults(func=CacheCommand, cache_command="delete") + + def __init__(self, args: Namespace) -> None: + self.args = args + self.verbosity: int = getattr(args, "verbose", 0) + self.cache_dir: Optional[str] = getattr(args, "dir", None) + self.disable_tui: bool = getattr(args, "disable_tui", False) + self.sort_by: Optional[SortingOption_T] = getattr(args, "sort", None) + self.cache_command: Optional[str] = getattr(args, "cache_command", None) + + def run(self): + if self.cache_command == "scan": + self._run_scan() + elif self.cache_command == "delete": + self._run_delete() + else: + print("Please specify a cache subcommand (scan or delete). Use -h for help.") + + def _run_scan(self): + try: + t0 = time.time() + hf_cache_info = scan_cache_dir(self.cache_dir) + t1 = time.time() + except CacheNotFound as exc: + cache_dir = exc.cache_dir + print(f"Cache directory not found: {cache_dir}") + return + print(get_table(hf_cache_info, verbosity=self.verbosity)) + print( + f"\nDone in {round(t1 - t0, 1)}s. Scanned {len(hf_cache_info.repos)} repo(s)" + f" for a total of {ANSI.red(hf_cache_info.size_on_disk_str)}." + ) + if len(hf_cache_info.warnings) > 0: + message = f"Got {len(hf_cache_info.warnings)} warning(s) while scanning." + if self.verbosity >= 3: + print(ANSI.gray(message)) + for warning in hf_cache_info.warnings: + print(ANSI.gray(str(warning))) + else: + print(ANSI.gray(message + " Use -vvv to print details.")) + + def _run_delete(self): + hf_cache_info = scan_cache_dir(self.cache_dir) + if self.disable_tui: + selected_hashes = _manual_review_no_tui(hf_cache_info, preselected=[], sort_by=self.sort_by) + else: + selected_hashes = _manual_review_tui(hf_cache_info, preselected=[], sort_by=self.sort_by) + if len(selected_hashes) > 0 and _CANCEL_DELETION_STR not in selected_hashes: + confirm_message = _get_expectations_str(hf_cache_info, selected_hashes) + " Confirm deletion ?" + if self.disable_tui: + confirmed = _ask_for_confirmation_no_tui(confirm_message) + else: + confirmed = _ask_for_confirmation_tui(confirm_message) + if confirmed: + strategy = hf_cache_info.delete_revisions(*selected_hashes) + print("Start deletion.") + strategy.execute() + print( + f"Done. Deleted {len(strategy.repos)} repo(s) and" + f" {len(strategy.snapshots)} revision(s) for a total of" + f" {strategy.expected_freed_size_str}." + ) + return + print("Deletion is cancelled. Do nothing.") + + +def get_table(hf_cache_info: HFCacheInfo, *, verbosity: int = 0) -> str: + if verbosity == 0: + return tabulate( + rows=[ + [ + repo.repo_id, + repo.repo_type, + "{:>12}".format(repo.size_on_disk_str), + repo.nb_files, + repo.last_accessed_str, + repo.last_modified_str, + ", ".join(sorted(repo.refs)), + str(repo.repo_path), + ] + for repo in sorted(hf_cache_info.repos, key=lambda repo: repo.repo_path) + ], + headers=[ + "REPO ID", + "REPO TYPE", + "SIZE ON DISK", + "NB FILES", + "LAST_ACCESSED", + "LAST_MODIFIED", + "REFS", + "LOCAL PATH", + ], + ) + else: + return tabulate( + rows=[ + [ + repo.repo_id, + repo.repo_type, + revision.commit_hash, + "{:>12}".format(revision.size_on_disk_str), + revision.nb_files, + revision.last_modified_str, + ", ".join(sorted(revision.refs)), + str(revision.snapshot_path), + ] + for repo in sorted(hf_cache_info.repos, key=lambda repo: repo.repo_path) + for revision in sorted(repo.revisions, key=lambda revision: revision.commit_hash) + ], + headers=[ + "REPO ID", + "REPO TYPE", + "REVISION", + "SIZE ON DISK", + "NB FILES", + "LAST_MODIFIED", + "REFS", + "LOCAL PATH", + ], + ) + + +def _get_repo_sorting_key(repo: CachedRepoInfo, sort_by: Optional[SortingOption_T] = None): + if sort_by == "alphabetical": + return (repo.repo_type, repo.repo_id.lower()) + elif sort_by == "lastUpdated": + return -max(rev.last_modified for rev in repo.revisions) + elif sort_by == "lastUsed": + return -repo.last_accessed + elif sort_by == "size": + return -repo.size_on_disk + else: + return (repo.repo_type, repo.repo_id) + + +@require_inquirer_py +def _manual_review_tui( + hf_cache_info: HFCacheInfo, preselected: List[str], sort_by: Optional[SortingOption_T] = None +) -> List[str]: + choices = _get_tui_choices_from_scan(repos=hf_cache_info.repos, preselected=preselected, sort_by=sort_by) + checkbox = inquirer.checkbox( + message="Select revisions to delete:", + choices=choices, + cycle=False, + height=100, + instruction=_get_expectations_str( + hf_cache_info, selected_hashes=[c.value for c in choices if isinstance(c, Choice) and c.enabled] + ), + long_instruction="Press to select, to validate and to quit without modification.", + transformer=lambda result: f"{len(result)} revision(s) selected.", + ) + + def _update_expectations(_): + checkbox._instruction = _get_expectations_str( + hf_cache_info, + selected_hashes=[choice["value"] for choice in checkbox.content_control.choices if choice["enabled"]], + ) + + checkbox.kb_func_lookup["toggle"].append({"func": _update_expectations}) + try: + return checkbox.execute() + except KeyboardInterrupt: + return [] + + +@require_inquirer_py +def _ask_for_confirmation_tui(message: str, default: bool = True) -> bool: + return inquirer.confirm(message, default=default).execute() + + +def _get_tui_choices_from_scan( + repos: Iterable[CachedRepoInfo], preselected: List[str], sort_by: Optional[SortingOption_T] = None +) -> List: + choices: List[Union["Choice", "Separator"]] = [] + choices.append( + Choice( + _CANCEL_DELETION_STR, name="None of the following (if selected, nothing will be deleted).", enabled=False + ) + ) + sorted_repos = sorted(repos, key=lambda repo: _get_repo_sorting_key(repo, sort_by)) + for repo in sorted_repos: + choices.append( + Separator( + f"\n{repo.repo_type.capitalize()} {repo.repo_id} ({repo.size_on_disk_str}, used {repo.last_accessed_str})" + ) + ) + for revision in sorted(repo.revisions, key=_revision_sorting_order): + choices.append( + Choice( + revision.commit_hash, + name=( + f"{revision.commit_hash[:8]}: {', '.join(sorted(revision.refs)) or '(detached)'} # modified {revision.last_modified_str}" + ), + enabled=revision.commit_hash in preselected, + ) + ) + return choices + + +def _manual_review_no_tui( + hf_cache_info: HFCacheInfo, preselected: List[str], sort_by: Optional[SortingOption_T] = None +) -> List[str]: + fd, tmp_path = mkstemp(suffix=".txt") + os.close(fd) + lines = [] + sorted_repos = sorted(hf_cache_info.repos, key=lambda repo: _get_repo_sorting_key(repo, sort_by)) + for repo in sorted_repos: + lines.append( + f"\n# {repo.repo_type.capitalize()} {repo.repo_id} ({repo.size_on_disk_str}, used {repo.last_accessed_str})" + ) + for revision in sorted(repo.revisions, key=_revision_sorting_order): + lines.append( + f"{'' if revision.commit_hash in preselected else '#'} {revision.commit_hash} # Refs: {', '.join(sorted(revision.refs)) or '(detached)'} # modified {revision.last_modified_str}" + ) + with open(tmp_path, "w") as f: + f.write(_MANUAL_REVIEW_NO_TUI_INSTRUCTIONS) + f.write("\n".join(lines)) + instructions = f""" + TUI is disabled. In order to select which revisions you want to delete, please edit + the following file using the text editor of your choice. Instructions for manual + editing are located at the beginning of the file. Edit the file, save it and confirm + to continue. + File to edit: {ANSI.bold(tmp_path)} + """ + print("\n".join(line.strip() for line in instructions.strip().split("\n"))) + while True: + selected_hashes = _read_manual_review_tmp_file(tmp_path) + if _ask_for_confirmation_no_tui( + _get_expectations_str(hf_cache_info, selected_hashes) + " Continue ?", default=False + ): + break + os.remove(tmp_path) + return sorted(selected_hashes) + + +def _ask_for_confirmation_no_tui(message: str, default: bool = True) -> bool: + YES = ("y", "yes", "1") + NO = ("n", "no", "0") + DEFAULT = "" + ALL = YES + NO + (DEFAULT,) + full_message = message + (" (Y/n) " if default else " (y/N) ") + while True: + answer = input(full_message).lower() + if answer == DEFAULT: + return default + if answer in YES: + return True + if answer in NO: + return False + print(f"Invalid input. Must be one of {ALL}") + + +def _get_expectations_str(hf_cache_info: HFCacheInfo, selected_hashes: List[str]) -> str: + if _CANCEL_DELETION_STR in selected_hashes: + return "Nothing will be deleted." + strategy = hf_cache_info.delete_revisions(*selected_hashes) + return f"{len(selected_hashes)} revisions selected counting for {strategy.expected_freed_size_str}." + + +def _read_manual_review_tmp_file(tmp_path: str) -> List[str]: + with open(tmp_path) as f: + content = f.read() + lines = [line.strip() for line in content.split("\n")] + selected_lines = [line for line in lines if not line.startswith("#")] + selected_hashes = [line.split("#")[0].strip() for line in selected_lines] + return [hash for hash in selected_hashes if len(hash) > 0] + + +_MANUAL_REVIEW_NO_TUI_INSTRUCTIONS = f""" +# INSTRUCTIONS +# ------------ +# This is a temporary file created by running `hf cache delete --disable-tui`. It contains a set of revisions that can be deleted from your local cache directory. +# +# Please manually review the revisions you want to delete: +# - Revision hashes can be commented out with '#'. +# - Only non-commented revisions in this file will be deleted. +# - Revision hashes that are removed from this file are ignored as well. +# - If `{_CANCEL_DELETION_STR}` line is uncommented, the all cache deletion is cancelled and no changes will be applied. +# +# Once you've manually reviewed this file, please confirm deletion in the terminal. This file will be automatically removed once done. +# ------------ + +# KILL SWITCH +# ------------ +# Un-comment following line to completely cancel the deletion process +# {_CANCEL_DELETION_STR} +# ------------ + +# REVISIONS +# ------------ +""".strip() + + +def _revision_sorting_order(revision: CachedRevisionInfo) -> Any: + return revision.last_modified diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/cli/download.py b/venv/lib/python3.13/site-packages/huggingface_hub/cli/download.py new file mode 100644 index 0000000000000000000000000000000000000000..2660644e62955952f010701a823d7a8bdce1803b --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/cli/download.py @@ -0,0 +1,181 @@ +# coding=utf-8 +# Copyright 202-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains command to download files from the Hub with the CLI. + +Usage: + hf download --help + + # Download file + hf download gpt2 config.json + + # Download entire repo + hf download fffiloni/zeroscope --repo-type=space --revision=refs/pr/78 + + # Download repo with filters + hf download gpt2 --include="*.safetensors" + + # Download with token + hf download Wauplin/private-model --token=hf_*** + + # Download quietly (no progress bar, no warnings, only the returned path) + hf download gpt2 config.json --quiet + + # Download to local dir + hf download gpt2 --local-dir=./models/gpt2 +""" + +import warnings +from argparse import Namespace, _SubParsersAction +from typing import List, Optional + +from huggingface_hub import logging +from huggingface_hub._snapshot_download import snapshot_download +from huggingface_hub.commands import BaseHuggingfaceCLICommand +from huggingface_hub.file_download import hf_hub_download +from huggingface_hub.utils import disable_progress_bars, enable_progress_bars + + +logger = logging.get_logger(__name__) + + +class DownloadCommand(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction): + download_parser = parser.add_parser("download", help="Download files from the Hub") + download_parser.add_argument( + "repo_id", type=str, help="ID of the repo to download from (e.g. `username/repo-name`)." + ) + download_parser.add_argument( + "filenames", type=str, nargs="*", help="Files to download (e.g. `config.json`, `data/metadata.jsonl`)." + ) + download_parser.add_argument( + "--repo-type", + choices=["model", "dataset", "space"], + default="model", + help="Type of repo to download from (defaults to 'model').", + ) + download_parser.add_argument( + "--revision", + type=str, + help="An optional Git revision id which can be a branch name, a tag, or a commit hash.", + ) + download_parser.add_argument( + "--include", nargs="*", type=str, help="Glob patterns to match files to download." + ) + download_parser.add_argument( + "--exclude", nargs="*", type=str, help="Glob patterns to exclude from files to download." + ) + download_parser.add_argument( + "--cache-dir", type=str, help="Path to the directory where to save the downloaded files." + ) + download_parser.add_argument( + "--local-dir", + type=str, + help=( + "If set, the downloaded file will be placed under this directory. Check out" + " https://huggingface.co/docs/huggingface_hub/guides/download#download-files-to-local-folder for more" + " details." + ), + ) + download_parser.add_argument( + "--force-download", + action="store_true", + help="If True, the files will be downloaded even if they are already cached.", + ) + download_parser.add_argument( + "--token", type=str, help="A User Access Token generated from https://huggingface.co/settings/tokens" + ) + download_parser.add_argument( + "--quiet", + action="store_true", + help="If True, progress bars are disabled and only the path to the download files is printed.", + ) + download_parser.add_argument( + "--max-workers", + type=int, + default=8, + help="Maximum number of workers to use for downloading files. Default is 8.", + ) + download_parser.set_defaults(func=DownloadCommand) + + def __init__(self, args: Namespace) -> None: + self.token = args.token + self.repo_id: str = args.repo_id + self.filenames: List[str] = args.filenames + self.repo_type: str = args.repo_type + self.revision: Optional[str] = args.revision + self.include: Optional[List[str]] = args.include + self.exclude: Optional[List[str]] = args.exclude + self.cache_dir: Optional[str] = args.cache_dir + self.local_dir: Optional[str] = args.local_dir + self.force_download: bool = args.force_download + self.quiet: bool = args.quiet + self.max_workers: int = args.max_workers + + def run(self) -> None: + if self.quiet: + disable_progress_bars() + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + print(self._download()) # Print path to downloaded files + enable_progress_bars() + else: + logging.set_verbosity_info() + print(self._download()) # Print path to downloaded files + logging.set_verbosity_warning() + + def _download(self) -> str: + # Warn user if patterns are ignored + if len(self.filenames) > 0: + if self.include is not None and len(self.include) > 0: + warnings.warn("Ignoring `--include` since filenames have being explicitly set.") + if self.exclude is not None and len(self.exclude) > 0: + warnings.warn("Ignoring `--exclude` since filenames have being explicitly set.") + + # Single file to download: use `hf_hub_download` + if len(self.filenames) == 1: + return hf_hub_download( + repo_id=self.repo_id, + repo_type=self.repo_type, + revision=self.revision, + filename=self.filenames[0], + cache_dir=self.cache_dir, + force_download=self.force_download, + token=self.token, + local_dir=self.local_dir, + library_name="huggingface-cli", + ) + + # Otherwise: use `snapshot_download` to ensure all files comes from same revision + elif len(self.filenames) == 0: + allow_patterns = self.include + ignore_patterns = self.exclude + else: + allow_patterns = self.filenames + ignore_patterns = None + + return snapshot_download( + repo_id=self.repo_id, + repo_type=self.repo_type, + revision=self.revision, + allow_patterns=allow_patterns, + ignore_patterns=ignore_patterns, + force_download=self.force_download, + cache_dir=self.cache_dir, + token=self.token, + local_dir=self.local_dir, + library_name="huggingface-cli", + max_workers=self.max_workers, + ) diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/cli/hf.py b/venv/lib/python3.13/site-packages/huggingface_hub/cli/hf.py new file mode 100644 index 0000000000000000000000000000000000000000..2587918b294b427fb8f3e0f990884826b66514a8 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/cli/hf.py @@ -0,0 +1,63 @@ +# Copyright 2020 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from argparse import ArgumentParser + +from huggingface_hub.cli.auth import AuthCommands +from huggingface_hub.cli.cache import CacheCommand +from huggingface_hub.cli.download import DownloadCommand +from huggingface_hub.cli.jobs import JobsCommands +from huggingface_hub.cli.lfs import LfsCommands +from huggingface_hub.cli.repo import RepoCommands +from huggingface_hub.cli.repo_files import RepoFilesCommand +from huggingface_hub.cli.system import EnvironmentCommand, VersionCommand +from huggingface_hub.cli.upload import UploadCommand +from huggingface_hub.cli.upload_large_folder import UploadLargeFolderCommand + + +def main(): + parser = ArgumentParser("hf", usage="hf []") + commands_parser = parser.add_subparsers(help="hf command helpers") + + # Register commands + AuthCommands.register_subcommand(commands_parser) + CacheCommand.register_subcommand(commands_parser) + DownloadCommand.register_subcommand(commands_parser) + JobsCommands.register_subcommand(commands_parser) + RepoCommands.register_subcommand(commands_parser) + RepoFilesCommand.register_subcommand(commands_parser) + UploadCommand.register_subcommand(commands_parser) + UploadLargeFolderCommand.register_subcommand(commands_parser) + + # System commands + EnvironmentCommand.register_subcommand(commands_parser) + VersionCommand.register_subcommand(commands_parser) + + # LFS commands (hidden in --help) + LfsCommands.register_subcommand(commands_parser) + + # Let's go + args = parser.parse_args() + if not hasattr(args, "func"): + parser.print_help() + exit(1) + + # Run + service = args.func(args) + if service is not None: + service.run() + + +if __name__ == "__main__": + main() diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/cli/jobs.py b/venv/lib/python3.13/site-packages/huggingface_hub/cli/jobs.py new file mode 100644 index 0000000000000000000000000000000000000000..3a661c7df7d65813dbb1b2a8f449ca8410e320e0 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/cli/jobs.py @@ -0,0 +1,1100 @@ +# Copyright 2025 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains commands to interact with jobs on the Hugging Face Hub. + +Usage: + # run a job + hf jobs run + + # List running or completed jobs + hf jobs ps [-a] [-f key=value] [--format TEMPLATE] + + # Stream logs from a job + hf jobs logs + + # Inspect detailed information about a job + hf jobs inspect + + # Cancel a running job + hf jobs cancel +""" + +import json +import os +import re +from argparse import Namespace, _SubParsersAction +from dataclasses import asdict +from pathlib import Path +from typing import Dict, List, Optional, Union + +import requests + +from huggingface_hub import HfApi, SpaceHardware, get_token +from huggingface_hub.utils import logging +from huggingface_hub.utils._dotenv import load_dotenv + +from . import BaseHuggingfaceCLICommand + + +logger = logging.get_logger(__name__) + +SUGGESTED_FLAVORS = [item.value for item in SpaceHardware if item.value != "zero-a10g"] + + +class JobsCommands(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction): + jobs_parser = parser.add_parser("jobs", help="Run and manage Jobs on the Hub.") + jobs_subparsers = jobs_parser.add_subparsers(help="huggingface.co jobs related commands") + + # Show help if no subcommand is provided + jobs_parser.set_defaults(func=lambda args: jobs_parser.print_help()) + + # Register commands + InspectCommand.register_subcommand(jobs_subparsers) + LogsCommand.register_subcommand(jobs_subparsers) + PsCommand.register_subcommand(jobs_subparsers) + RunCommand.register_subcommand(jobs_subparsers) + CancelCommand.register_subcommand(jobs_subparsers) + UvCommand.register_subcommand(jobs_subparsers) + ScheduledJobsCommands.register_subcommand(jobs_subparsers) + + +class RunCommand(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction) -> None: + run_parser = parser.add_parser("run", help="Run a Job") + run_parser.add_argument("image", type=str, help="The Docker image to use.") + run_parser.add_argument("-e", "--env", action="append", help="Set environment variables. E.g. --env ENV=value") + run_parser.add_argument( + "-s", + "--secrets", + action="append", + help=( + "Set secret environment variables. E.g. --secrets SECRET=value " + "or `--secrets HF_TOKEN` to pass your Hugging Face token." + ), + ) + run_parser.add_argument("--env-file", type=str, help="Read in a file of environment variables.") + run_parser.add_argument("--secrets-file", type=str, help="Read in a file of secret environment variables.") + run_parser.add_argument( + "--flavor", + type=str, + help=f"Flavor for the hardware, as in HF Spaces. Defaults to `cpu-basic`. Possible values: {', '.join(SUGGESTED_FLAVORS)}.", + ) + run_parser.add_argument( + "--timeout", + type=str, + help="Max duration: int/float with s (seconds, default), m (minutes), h (hours) or d (days).", + ) + run_parser.add_argument( + "-d", + "--detach", + action="store_true", + help="Run the Job in the background and print the Job ID.", + ) + run_parser.add_argument( + "--namespace", + type=str, + help="The namespace where the Job will be created. Defaults to the current user's namespace.", + ) + run_parser.add_argument( + "--token", + type=str, + help="A User Access Token generated from https://huggingface.co/settings/tokens", + ) + run_parser.add_argument("command", nargs="...", help="The command to run.") + run_parser.set_defaults(func=RunCommand) + + def __init__(self, args: Namespace) -> None: + self.image: str = args.image + self.command: List[str] = args.command + self.env: dict[str, Optional[str]] = {} + if args.env_file: + self.env.update(load_dotenv(Path(args.env_file).read_text(), environ=os.environ.copy())) + for env_value in args.env or []: + self.env.update(load_dotenv(env_value, environ=os.environ.copy())) + self.secrets: dict[str, Optional[str]] = {} + extended_environ = _get_extended_environ() + if args.secrets_file: + self.secrets.update(load_dotenv(Path(args.secrets_file).read_text(), environ=extended_environ)) + for secret in args.secrets or []: + self.secrets.update(load_dotenv(secret, environ=extended_environ)) + self.flavor: Optional[SpaceHardware] = args.flavor + self.timeout: Optional[str] = args.timeout + self.detach: bool = args.detach + self.namespace: Optional[str] = args.namespace + self.token: Optional[str] = args.token + + def run(self) -> None: + api = HfApi(token=self.token) + job = api.run_job( + image=self.image, + command=self.command, + env=self.env, + secrets=self.secrets, + flavor=self.flavor, + timeout=self.timeout, + namespace=self.namespace, + ) + # Always print the job ID to the user + print(f"Job started with ID: {job.id}") + print(f"View at: {job.url}") + + if self.detach: + return + + # Now let's stream the logs + for log in api.fetch_job_logs(job_id=job.id): + print(log) + + +class LogsCommand(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction) -> None: + run_parser = parser.add_parser("logs", help="Fetch the logs of a Job") + run_parser.add_argument("job_id", type=str, help="Job ID") + run_parser.add_argument( + "--namespace", + type=str, + help="The namespace where the job is running. Defaults to the current user's namespace.", + ) + run_parser.add_argument( + "--token", type=str, help="A User Access Token generated from https://huggingface.co/settings/tokens" + ) + run_parser.set_defaults(func=LogsCommand) + + def __init__(self, args: Namespace) -> None: + self.job_id: str = args.job_id + self.namespace: Optional[str] = args.namespace + self.token: Optional[str] = args.token + + def run(self) -> None: + api = HfApi(token=self.token) + for log in api.fetch_job_logs(job_id=self.job_id, namespace=self.namespace): + print(log) + + +def _tabulate(rows: List[List[Union[str, int]]], headers: List[str]) -> str: + """ + Inspired by: + + - stackoverflow.com/a/8356620/593036 + - stackoverflow.com/questions/9535954/printing-lists-as-tabular-data + """ + col_widths = [max(len(str(x)) for x in col) for col in zip(*rows, headers)] + terminal_width = max(os.get_terminal_size().columns, len(headers) * 12) + while len(headers) + sum(col_widths) > terminal_width: + col_to_minimize = col_widths.index(max(col_widths)) + col_widths[col_to_minimize] //= 2 + if len(headers) + sum(col_widths) <= terminal_width: + col_widths[col_to_minimize] = terminal_width - sum(col_widths) - len(headers) + col_widths[col_to_minimize] + row_format = ("{{:{}}} " * len(headers)).format(*col_widths) + lines = [] + lines.append(row_format.format(*headers)) + lines.append(row_format.format(*["-" * w for w in col_widths])) + for row in rows: + row_format_args = [ + str(x)[: col_width - 3] + "..." if len(str(x)) > col_width else str(x) + for x, col_width in zip(row, col_widths) + ] + lines.append(row_format.format(*row_format_args)) + return "\n".join(lines) + + +class PsCommand(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction) -> None: + run_parser = parser.add_parser("ps", help="List Jobs") + run_parser.add_argument( + "-a", + "--all", + action="store_true", + help="Show all Jobs (default shows just running)", + ) + run_parser.add_argument( + "--namespace", + type=str, + help="The namespace from where it lists the jobs. Defaults to the current user's namespace.", + ) + run_parser.add_argument( + "--token", + type=str, + help="A User Access Token generated from https://huggingface.co/settings/tokens", + ) + # Add Docker-style filtering argument + run_parser.add_argument( + "-f", + "--filter", + action="append", + default=[], + help="Filter output based on conditions provided (format: key=value)", + ) + # Add option to format output + run_parser.add_argument( + "--format", + type=str, + help="Format output using a custom template", + ) + run_parser.set_defaults(func=PsCommand) + + def __init__(self, args: Namespace) -> None: + self.all: bool = args.all + self.namespace: Optional[str] = args.namespace + self.token: Optional[str] = args.token + self.format: Optional[str] = args.format + self.filters: Dict[str, str] = {} + + # Parse filter arguments (key=value pairs) + for f in args.filter: + if "=" in f: + key, value = f.split("=", 1) + self.filters[key.lower()] = value + else: + print(f"Warning: Ignoring invalid filter format '{f}'. Use key=value format.") + + def run(self) -> None: + """ + Fetch and display job information for the current user. + Uses Docker-style filtering with -f/--filter flag and key=value pairs. + """ + try: + api = HfApi(token=self.token) + + # Fetch jobs data + jobs = api.list_jobs(namespace=self.namespace) + + # Define table headers + table_headers = ["JOB ID", "IMAGE/SPACE", "COMMAND", "CREATED", "STATUS"] + + # Process jobs data + rows = [] + + for job in jobs: + # Extract job data for filtering + status = job.status.stage if job.status else "UNKNOWN" + + # Skip job if not all jobs should be shown and status doesn't match criteria + if not self.all and status not in ("RUNNING", "UPDATING"): + continue + + # Extract job ID + job_id = job.id + + # Extract image or space information + image_or_space = job.docker_image or "N/A" + + # Extract and format command + command = job.command or [] + command_str = " ".join(command) if command else "N/A" + + # Extract creation time + created_at = job.created_at.strftime("%Y-%m-%d %H:%M:%S") if job.created_at else "N/A" + + # Create a dict with all job properties for filtering + job_properties = { + "id": job_id, + "image": image_or_space, + "status": status.lower(), + "command": command_str, + } + + # Check if job matches all filters + if not self._matches_filters(job_properties): + continue + + # Create row + rows.append([job_id, image_or_space, command_str, created_at, status]) + + # Handle empty results + if not rows: + filters_msg = "" + if self.filters: + filters_msg = f" matching filters: {', '.join([f'{k}={v}' for k, v in self.filters.items()])}" + + print(f"No jobs found{filters_msg}") + return + + # Apply custom format if provided or use default tabular format + self._print_output(rows, table_headers) + + except requests.RequestException as e: + print(f"Error fetching jobs data: {e}") + except (KeyError, ValueError, TypeError) as e: + print(f"Error processing jobs data: {e}") + except Exception as e: + print(f"Unexpected error - {type(e).__name__}: {e}") + + def _matches_filters(self, job_properties: Dict[str, str]) -> bool: + """Check if job matches all specified filters.""" + for key, pattern in self.filters.items(): + # Check if property exists + if key not in job_properties: + return False + + # Support pattern matching with wildcards + if "*" in pattern or "?" in pattern: + # Convert glob pattern to regex + regex_pattern = pattern.replace("*", ".*").replace("?", ".") + if not re.search(f"^{regex_pattern}$", job_properties[key], re.IGNORECASE): + return False + # Simple substring matching + elif pattern.lower() not in job_properties[key].lower(): + return False + + return True + + def _print_output(self, rows, headers): + """Print output according to the chosen format.""" + if self.format: + # Custom template formatting (simplified) + template = self.format + for row in rows: + line = template + for i, field in enumerate(["id", "image", "command", "created", "status"]): + placeholder = f"{{{{.{field}}}}}" + if placeholder in line: + line = line.replace(placeholder, str(row[i])) + print(line) + else: + # Default tabular format + print( + _tabulate( + rows, + headers=headers, + ) + ) + + +class InspectCommand(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction) -> None: + run_parser = parser.add_parser("inspect", help="Display detailed information on one or more Jobs") + run_parser.add_argument( + "--namespace", + type=str, + help="The namespace where the job is running. Defaults to the current user's namespace.", + ) + run_parser.add_argument( + "--token", type=str, help="A User Access Token generated from https://huggingface.co/settings/tokens" + ) + run_parser.add_argument("job_ids", nargs="...", help="The jobs to inspect") + run_parser.set_defaults(func=InspectCommand) + + def __init__(self, args: Namespace) -> None: + self.namespace: Optional[str] = args.namespace + self.token: Optional[str] = args.token + self.job_ids: List[str] = args.job_ids + + def run(self) -> None: + api = HfApi(token=self.token) + jobs = [api.inspect_job(job_id=job_id, namespace=self.namespace) for job_id in self.job_ids] + print(json.dumps([asdict(job) for job in jobs], indent=4, default=str)) + + +class CancelCommand(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction) -> None: + run_parser = parser.add_parser("cancel", help="Cancel a Job") + run_parser.add_argument("job_id", type=str, help="Job ID") + run_parser.add_argument( + "--namespace", + type=str, + help="The namespace where the job is running. Defaults to the current user's namespace.", + ) + run_parser.add_argument( + "--token", type=str, help="A User Access Token generated from https://huggingface.co/settings/tokens" + ) + run_parser.set_defaults(func=CancelCommand) + + def __init__(self, args: Namespace) -> None: + self.job_id: str = args.job_id + self.namespace = args.namespace + self.token: Optional[str] = args.token + + def run(self) -> None: + api = HfApi(token=self.token) + api.cancel_job(job_id=self.job_id, namespace=self.namespace) + + +class UvCommand(BaseHuggingfaceCLICommand): + """Run UV scripts on Hugging Face infrastructure.""" + + @staticmethod + def register_subcommand(parser): + """Register UV run subcommand.""" + uv_parser = parser.add_parser( + "uv", + help="Run UV scripts (Python with inline dependencies) on HF infrastructure", + ) + + subparsers = uv_parser.add_subparsers(dest="uv_command", help="UV commands", required=True) + + # Run command only + run_parser = subparsers.add_parser( + "run", + help="Run a UV script (local file or URL) on HF infrastructure", + ) + run_parser.add_argument("script", help="UV script to run (local file or URL)") + run_parser.add_argument("script_args", nargs="...", help="Arguments for the script", default=[]) + run_parser.add_argument("--image", type=str, help="Use a custom Docker image with `uv` installed.") + run_parser.add_argument( + "--repo", + help="Repository name for the script (creates ephemeral if not specified)", + ) + run_parser.add_argument( + "--flavor", + type=str, + help=f"Flavor for the hardware, as in HF Spaces. Defaults to `cpu-basic`. Possible values: {', '.join(SUGGESTED_FLAVORS)}.", + ) + run_parser.add_argument("-e", "--env", action="append", help="Environment variables") + run_parser.add_argument( + "-s", + "--secrets", + action="append", + help=( + "Set secret environment variables. E.g. --secrets SECRET=value " + "or `--secrets HF_TOKEN` to pass your Hugging Face token." + ), + ) + run_parser.add_argument("--env-file", type=str, help="Read in a file of environment variables.") + run_parser.add_argument( + "--secrets-file", + type=str, + help="Read in a file of secret environment variables.", + ) + run_parser.add_argument("--timeout", type=str, help="Max duration (e.g., 30s, 5m, 1h)") + run_parser.add_argument("-d", "--detach", action="store_true", help="Run in background") + run_parser.add_argument( + "--namespace", + type=str, + help="The namespace where the Job will be created. Defaults to the current user's namespace.", + ) + run_parser.add_argument("--token", type=str, help="HF token") + # UV options + run_parser.add_argument("--with", action="append", help="Run with the given packages installed", dest="with_") + run_parser.add_argument( + "-p", "--python", type=str, help="The Python interpreter to use for the run environment" + ) + run_parser.set_defaults(func=UvCommand) + + def __init__(self, args: Namespace) -> None: + """Initialize the command with parsed arguments.""" + self.script = args.script + self.script_args = args.script_args + self.dependencies = args.with_ + self.python = args.python + self.image = args.image + self.env: dict[str, Optional[str]] = {} + if args.env_file: + self.env.update(load_dotenv(Path(args.env_file).read_text(), environ=os.environ.copy())) + for env_value in args.env or []: + self.env.update(load_dotenv(env_value, environ=os.environ.copy())) + self.secrets: dict[str, Optional[str]] = {} + extended_environ = _get_extended_environ() + if args.secrets_file: + self.secrets.update(load_dotenv(Path(args.secrets_file).read_text(), environ=extended_environ)) + for secret in args.secrets or []: + self.secrets.update(load_dotenv(secret, environ=extended_environ)) + self.flavor: Optional[SpaceHardware] = args.flavor + self.timeout: Optional[str] = args.timeout + self.detach: bool = args.detach + self.namespace: Optional[str] = args.namespace + self.token: Optional[str] = args.token + self._repo = args.repo + + def run(self) -> None: + """Execute UV command.""" + logging.set_verbosity(logging.INFO) + api = HfApi(token=self.token) + job = api.run_uv_job( + script=self.script, + script_args=self.script_args, + dependencies=self.dependencies, + python=self.python, + image=self.image, + env=self.env, + secrets=self.secrets, + flavor=self.flavor, + timeout=self.timeout, + namespace=self.namespace, + _repo=self._repo, + ) + + # Always print the job ID to the user + print(f"Job started with ID: {job.id}") + print(f"View at: {job.url}") + + if self.detach: + return + + # Now let's stream the logs + for log in api.fetch_job_logs(job_id=job.id): + print(log) + + +def _get_extended_environ() -> Dict[str, str]: + extended_environ = os.environ.copy() + if (token := get_token()) is not None: + extended_environ["HF_TOKEN"] = token + return extended_environ + + +class ScheduledJobsCommands(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction): + scheduled_jobs_parser = parser.add_parser("scheduled", help="Create and manage scheduled Jobs on the Hub.") + scheduled_jobs_subparsers = scheduled_jobs_parser.add_subparsers( + help="huggingface.co scheduled jobs related commands" + ) + + # Show help if no subcommand is provided + scheduled_jobs_parser.set_defaults(func=lambda args: scheduled_jobs_subparsers.print_help()) + + # Register commands + ScheduledRunCommand.register_subcommand(scheduled_jobs_subparsers) + ScheduledPsCommand.register_subcommand(scheduled_jobs_subparsers) + ScheduledInspectCommand.register_subcommand(scheduled_jobs_subparsers) + ScheduledDeleteCommand.register_subcommand(scheduled_jobs_subparsers) + ScheduledSuspendCommand.register_subcommand(scheduled_jobs_subparsers) + ScheduledResumeCommand.register_subcommand(scheduled_jobs_subparsers) + ScheduledUvCommand.register_subcommand(scheduled_jobs_subparsers) + + +class ScheduledRunCommand(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction) -> None: + run_parser = parser.add_parser("run", help="Schedule a Job") + run_parser.add_argument( + "schedule", + type=str, + help="One of annually, yearly, monthly, weekly, daily, hourly, or a CRON schedule expression.", + ) + run_parser.add_argument("image", type=str, help="The Docker image to use.") + run_parser.add_argument( + "--suspend", + action="store_true", + help="Suspend (pause) the scheduled Job", + default=None, + ) + run_parser.add_argument( + "--concurrency", + action="store_true", + help="Allow multiple instances of this Job to run concurrently", + default=None, + ) + run_parser.add_argument("-e", "--env", action="append", help="Set environment variables. E.g. --env ENV=value") + run_parser.add_argument( + "-s", + "--secrets", + action="append", + help=( + "Set secret environment variables. E.g. --secrets SECRET=value " + "or `--secrets HF_TOKEN` to pass your Hugging Face token." + ), + ) + run_parser.add_argument("--env-file", type=str, help="Read in a file of environment variables.") + run_parser.add_argument("--secrets-file", type=str, help="Read in a file of secret environment variables.") + run_parser.add_argument( + "--flavor", + type=str, + help=f"Flavor for the hardware, as in HF Spaces. Defaults to `cpu-basic`. Possible values: {', '.join(SUGGESTED_FLAVORS)}.", + ) + run_parser.add_argument( + "--timeout", + type=str, + help="Max duration: int/float with s (seconds, default), m (minutes), h (hours) or d (days).", + ) + run_parser.add_argument( + "--namespace", + type=str, + help="The namespace where the scheduled Job will be created. Defaults to the current user's namespace.", + ) + run_parser.add_argument( + "--token", + type=str, + help="A User Access Token generated from https://huggingface.co/settings/tokens", + ) + run_parser.add_argument("command", nargs="...", help="The command to run.") + run_parser.set_defaults(func=ScheduledRunCommand) + + def __init__(self, args: Namespace) -> None: + self.schedule: str = args.schedule + self.image: str = args.image + self.command: List[str] = args.command + self.suspend: Optional[bool] = args.suspend + self.concurrency: Optional[bool] = args.concurrency + self.env: dict[str, Optional[str]] = {} + if args.env_file: + self.env.update(load_dotenv(Path(args.env_file).read_text(), environ=os.environ.copy())) + for env_value in args.env or []: + self.env.update(load_dotenv(env_value, environ=os.environ.copy())) + self.secrets: dict[str, Optional[str]] = {} + extended_environ = _get_extended_environ() + if args.secrets_file: + self.secrets.update(load_dotenv(Path(args.secrets_file).read_text(), environ=extended_environ)) + for secret in args.secrets or []: + self.secrets.update(load_dotenv(secret, environ=extended_environ)) + self.flavor: Optional[SpaceHardware] = args.flavor + self.timeout: Optional[str] = args.timeout + self.namespace: Optional[str] = args.namespace + self.token: Optional[str] = args.token + + def run(self) -> None: + api = HfApi(token=self.token) + scheduled_job = api.create_scheduled_job( + image=self.image, + command=self.command, + schedule=self.schedule, + suspend=self.suspend, + concurrency=self.concurrency, + env=self.env, + secrets=self.secrets, + flavor=self.flavor, + timeout=self.timeout, + namespace=self.namespace, + ) + # Always print the scheduled job ID to the user + print(f"Scheduled Job created with ID: {scheduled_job.id}") + + +class ScheduledPsCommand(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction) -> None: + run_parser = parser.add_parser("ps", help="List scheduled Jobs") + run_parser.add_argument( + "-a", + "--all", + action="store_true", + help="Show all scheduled Jobs (default hides suspended)", + ) + run_parser.add_argument( + "--namespace", + type=str, + help="The namespace from where it lists the jobs. Defaults to the current user's namespace.", + ) + run_parser.add_argument( + "--token", + type=str, + help="A User Access Token generated from https://huggingface.co/settings/tokens", + ) + # Add Docker-style filtering argument + run_parser.add_argument( + "-f", + "--filter", + action="append", + default=[], + help="Filter output based on conditions provided (format: key=value)", + ) + # Add option to format output + run_parser.add_argument( + "--format", + type=str, + help="Format output using a custom template", + ) + run_parser.set_defaults(func=ScheduledPsCommand) + + def __init__(self, args: Namespace) -> None: + self.all: bool = args.all + self.namespace: Optional[str] = args.namespace + self.token: Optional[str] = args.token + self.format: Optional[str] = args.format + self.filters: Dict[str, str] = {} + + # Parse filter arguments (key=value pairs) + for f in args.filter: + if "=" in f: + key, value = f.split("=", 1) + self.filters[key.lower()] = value + else: + print(f"Warning: Ignoring invalid filter format '{f}'. Use key=value format.") + + def run(self) -> None: + """ + Fetch and display scheduked job information for the current user. + Uses Docker-style filtering with -f/--filter flag and key=value pairs. + """ + try: + api = HfApi(token=self.token) + + # Fetch jobs data + scheduled_jobs = api.list_scheduled_jobs(namespace=self.namespace) + + # Define table headers + table_headers = [ + "ID", + "SCHEDULE", + "IMAGE/SPACE", + "COMMAND", + "LAST RUN", + "NEXT RUN", + "SUSPEND", + ] + + # Process jobs data + rows = [] + + for scheduled_job in scheduled_jobs: + # Extract job data for filtering + suspend = scheduled_job.suspend + + # Skip job if not all jobs should be shown and status doesn't match criteria + if not self.all and suspend: + continue + + # Extract job ID + scheduled_job_id = scheduled_job.id + + # Extract schedule + schedule = scheduled_job.schedule + + # Extract image or space information + image_or_space = scheduled_job.job_spec.docker_image or "N/A" + + # Extract and format command + command = scheduled_job.job_spec.command or [] + command_str = " ".join(command) if command else "N/A" + + # Extract status + last_job_at = ( + scheduled_job.status.last_job.at.strftime("%Y-%m-%d %H:%M:%S") + if scheduled_job.status.last_job + else "N/A" + ) + next_job_run_at = ( + scheduled_job.status.next_job_run_at.strftime("%Y-%m-%d %H:%M:%S") + if scheduled_job.status.next_job_run_at + else "N/A" + ) + + # Create a dict with all job properties for filtering + job_properties = { + "id": scheduled_job_id, + "image": image_or_space, + "suspend": str(suspend), + "command": command_str, + } + + # Check if job matches all filters + if not self._matches_filters(job_properties): + continue + + # Create row + rows.append( + [ + scheduled_job_id, + schedule, + image_or_space, + command_str, + last_job_at, + next_job_run_at, + suspend, + ] + ) + + # Handle empty results + if not rows: + filters_msg = "" + if self.filters: + filters_msg = f" matching filters: {', '.join([f'{k}={v}' for k, v in self.filters.items()])}" + + print(f"No scheduled jobs found{filters_msg}") + return + + # Apply custom format if provided or use default tabular format + self._print_output(rows, table_headers) + + except requests.RequestException as e: + print(f"Error fetching scheduled jobs data: {e}") + except (KeyError, ValueError, TypeError) as e: + print(f"Error processing scheduled jobs data: {e}") + except Exception as e: + print(f"Unexpected error - {type(e).__name__}: {e}") + + def _matches_filters(self, job_properties: Dict[str, str]) -> bool: + """Check if scheduled job matches all specified filters.""" + for key, pattern in self.filters.items(): + # Check if property exists + if key not in job_properties: + return False + + # Support pattern matching with wildcards + if "*" in pattern or "?" in pattern: + # Convert glob pattern to regex + regex_pattern = pattern.replace("*", ".*").replace("?", ".") + if not re.search(f"^{regex_pattern}$", job_properties[key], re.IGNORECASE): + return False + # Simple substring matching + elif pattern.lower() not in job_properties[key].lower(): + return False + + return True + + def _print_output(self, rows, headers): + """Print output according to the chosen format.""" + if self.format: + # Custom template formatting (simplified) + template = self.format + for row in rows: + line = template + for i, field in enumerate( + ["id", "schedule", "image", "command", "last_job_at", "next_job_run_at", "suspend"] + ): + placeholder = f"{{{{.{field}}}}}" + if placeholder in line: + line = line.replace(placeholder, str(row[i])) + print(line) + else: + # Default tabular format + print( + _tabulate( + rows, + headers=headers, + ) + ) + + +class ScheduledInspectCommand(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction) -> None: + run_parser = parser.add_parser("inspect", help="Display detailed information on one or more scheduled Jobs") + run_parser.add_argument( + "--namespace", + type=str, + help="The namespace where the scheduled job is. Defaults to the current user's namespace.", + ) + run_parser.add_argument( + "--token", type=str, help="A User Access Token generated from https://huggingface.co/settings/tokens" + ) + run_parser.add_argument("scheduled_job_ids", nargs="...", help="The scheduled jobs to inspect") + run_parser.set_defaults(func=ScheduledInspectCommand) + + def __init__(self, args: Namespace) -> None: + self.namespace: Optional[str] = args.namespace + self.token: Optional[str] = args.token + self.scheduled_job_ids: List[str] = args.scheduled_job_ids + + def run(self) -> None: + api = HfApi(token=self.token) + scheduled_jobs = [ + api.inspect_scheduled_job(scheduled_job_id=scheduled_job_id, namespace=self.namespace) + for scheduled_job_id in self.scheduled_job_ids + ] + print(json.dumps([asdict(scheduled_job) for scheduled_job in scheduled_jobs], indent=4, default=str)) + + +class ScheduledDeleteCommand(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction) -> None: + run_parser = parser.add_parser("delete", help="Delete a scheduled Job") + run_parser.add_argument("scheduled_job_id", type=str, help="Scheduled Job ID") + run_parser.add_argument( + "--namespace", + type=str, + help="The namespace where the scheduled job is. Defaults to the current user's namespace.", + ) + run_parser.add_argument( + "--token", type=str, help="A User Access Token generated from https://huggingface.co/settings/tokens" + ) + run_parser.set_defaults(func=ScheduledDeleteCommand) + + def __init__(self, args: Namespace) -> None: + self.scheduled_job_id: str = args.scheduled_job_id + self.namespace = args.namespace + self.token: Optional[str] = args.token + + def run(self) -> None: + api = HfApi(token=self.token) + api.delete_scheduled_job(scheduled_job_id=self.scheduled_job_id, namespace=self.namespace) + + +class ScheduledSuspendCommand(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction) -> None: + run_parser = parser.add_parser("suspend", help="Suspend (pause) a scheduled Job") + run_parser.add_argument("scheduled_job_id", type=str, help="Scheduled Job ID") + run_parser.add_argument( + "--namespace", + type=str, + help="The namespace where the scheduled job is. Defaults to the current user's namespace.", + ) + run_parser.add_argument( + "--token", type=str, help="A User Access Token generated from https://huggingface.co/settings/tokens" + ) + run_parser.set_defaults(func=ScheduledSuspendCommand) + + def __init__(self, args: Namespace) -> None: + self.scheduled_job_id: str = args.scheduled_job_id + self.namespace = args.namespace + self.token: Optional[str] = args.token + + def run(self) -> None: + api = HfApi(token=self.token) + api.suspend_scheduled_job(scheduled_job_id=self.scheduled_job_id, namespace=self.namespace) + + +class ScheduledResumeCommand(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction) -> None: + run_parser = parser.add_parser("resume", help="Resume (unpause) a scheduled Job") + run_parser.add_argument("scheduled_job_id", type=str, help="Scheduled Job ID") + run_parser.add_argument( + "--namespace", + type=str, + help="The namespace where the scheduled job is. Defaults to the current user's namespace.", + ) + run_parser.add_argument( + "--token", type=str, help="A User Access Token generated from https://huggingface.co/settings/tokens" + ) + run_parser.set_defaults(func=ScheduledResumeCommand) + + def __init__(self, args: Namespace) -> None: + self.scheduled_job_id: str = args.scheduled_job_id + self.namespace = args.namespace + self.token: Optional[str] = args.token + + def run(self) -> None: + api = HfApi(token=self.token) + api.resume_scheduled_job(scheduled_job_id=self.scheduled_job_id, namespace=self.namespace) + + +class ScheduledUvCommand(BaseHuggingfaceCLICommand): + """Schedule UV scripts on Hugging Face infrastructure.""" + + @staticmethod + def register_subcommand(parser): + """Register UV run subcommand.""" + uv_parser = parser.add_parser( + "uv", + help="Schedule UV scripts (Python with inline dependencies) on HF infrastructure", + ) + + subparsers = uv_parser.add_subparsers(dest="uv_command", help="UV commands", required=True) + + # Run command only + run_parser = subparsers.add_parser( + "run", + help="Run a UV script (local file or URL) on HF infrastructure", + ) + run_parser.add_argument( + "schedule", + type=str, + help="One of annually, yearly, monthly, weekly, daily, hourly, or a CRON schedule expression.", + ) + run_parser.add_argument("script", help="UV script to run (local file or URL)") + run_parser.add_argument("script_args", nargs="...", help="Arguments for the script", default=[]) + run_parser.add_argument( + "--suspend", + action="store_true", + help="Suspend (pause) the scheduled Job", + default=None, + ) + run_parser.add_argument( + "--concurrency", + action="store_true", + help="Allow multiple instances of this Job to run concurrently", + default=None, + ) + run_parser.add_argument("--image", type=str, help="Use a custom Docker image with `uv` installed.") + run_parser.add_argument( + "--repo", + help="Repository name for the script (creates ephemeral if not specified)", + ) + run_parser.add_argument( + "--flavor", + type=str, + help=f"Flavor for the hardware, as in HF Spaces. Defaults to `cpu-basic`. Possible values: {', '.join(SUGGESTED_FLAVORS)}.", + ) + run_parser.add_argument("-e", "--env", action="append", help="Environment variables") + run_parser.add_argument( + "-s", + "--secrets", + action="append", + help=( + "Set secret environment variables. E.g. --secrets SECRET=value " + "or `--secrets HF_TOKEN` to pass your Hugging Face token." + ), + ) + run_parser.add_argument("--env-file", type=str, help="Read in a file of environment variables.") + run_parser.add_argument( + "--secrets-file", + type=str, + help="Read in a file of secret environment variables.", + ) + run_parser.add_argument("--timeout", type=str, help="Max duration (e.g., 30s, 5m, 1h)") + run_parser.add_argument("-d", "--detach", action="store_true", help="Run in background") + run_parser.add_argument( + "--namespace", + type=str, + help="The namespace where the Job will be created. Defaults to the current user's namespace.", + ) + run_parser.add_argument("--token", type=str, help="HF token") + # UV options + run_parser.add_argument("--with", action="append", help="Run with the given packages installed", dest="with_") + run_parser.add_argument( + "-p", "--python", type=str, help="The Python interpreter to use for the run environment" + ) + run_parser.set_defaults(func=ScheduledUvCommand) + + def __init__(self, args: Namespace) -> None: + """Initialize the command with parsed arguments.""" + self.schedule: str = args.schedule + self.script = args.script + self.script_args = args.script_args + self.suspend: Optional[bool] = args.suspend + self.concurrency: Optional[bool] = args.concurrency + self.dependencies = args.with_ + self.python = args.python + self.image = args.image + self.env: dict[str, Optional[str]] = {} + if args.env_file: + self.env.update(load_dotenv(Path(args.env_file).read_text(), environ=os.environ.copy())) + for env_value in args.env or []: + self.env.update(load_dotenv(env_value, environ=os.environ.copy())) + self.secrets: dict[str, Optional[str]] = {} + extended_environ = _get_extended_environ() + if args.secrets_file: + self.secrets.update(load_dotenv(Path(args.secrets_file).read_text(), environ=extended_environ)) + for secret in args.secrets or []: + self.secrets.update(load_dotenv(secret, environ=extended_environ)) + self.flavor: Optional[SpaceHardware] = args.flavor + self.timeout: Optional[str] = args.timeout + self.detach: bool = args.detach + self.namespace: Optional[str] = args.namespace + self.token: Optional[str] = args.token + self._repo = args.repo + + def run(self) -> None: + """Schedule UV command.""" + logging.set_verbosity(logging.INFO) + api = HfApi(token=self.token) + job = api.create_scheduled_uv_job( + script=self.script, + script_args=self.script_args, + schedule=self.schedule, + suspend=self.suspend, + concurrency=self.concurrency, + dependencies=self.dependencies, + python=self.python, + image=self.image, + env=self.env, + secrets=self.secrets, + flavor=self.flavor, + timeout=self.timeout, + namespace=self.namespace, + _repo=self._repo, + ) + + # Always print the job ID to the user + print(f"Scheduled Job created with ID: {job.id}") diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/cli/lfs.py b/venv/lib/python3.13/site-packages/huggingface_hub/cli/lfs.py new file mode 100644 index 0000000000000000000000000000000000000000..e4c5b900c816494c260f6c440843a2d83703fab5 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/cli/lfs.py @@ -0,0 +1,198 @@ +""" +Implementation of a custom transfer agent for the transfer type "multipart" for +git-lfs. + +Inspired by: +github.com/cbartz/git-lfs-swift-transfer-agent/blob/master/git_lfs_swift_transfer.py + +Spec is: github.com/git-lfs/git-lfs/blob/master/docs/custom-transfers.md + + +To launch debugger while developing: + +``` [lfs "customtransfer.multipart"] +path = /path/to/huggingface_hub/.env/bin/python args = -m debugpy --listen 5678 +--wait-for-client +/path/to/huggingface_hub/src/huggingface_hub/commands/huggingface_cli.py +lfs-multipart-upload ```""" + +import json +import os +import subprocess +import sys +from argparse import _SubParsersAction +from typing import Dict, List, Optional + +from huggingface_hub.commands import BaseHuggingfaceCLICommand +from huggingface_hub.lfs import LFS_MULTIPART_UPLOAD_COMMAND + +from ..utils import get_session, hf_raise_for_status, logging +from ..utils._lfs import SliceFileObj + + +logger = logging.get_logger(__name__) + + +class LfsCommands(BaseHuggingfaceCLICommand): + """ + Implementation of a custom transfer agent for the transfer type "multipart" + for git-lfs. This lets users upload large files >5GB 🔥. Spec for LFS custom + transfer agent is: + https://github.com/git-lfs/git-lfs/blob/master/docs/custom-transfers.md + + This introduces two commands to the CLI: + + 1. $ hf lfs-enable-largefiles + + This should be executed once for each model repo that contains a model file + >5GB. It's documented in the error message you get if you just try to git + push a 5GB file without having enabled it before. + + 2. $ hf lfs-multipart-upload + + This command is called by lfs directly and is not meant to be called by the + user. + """ + + @staticmethod + def register_subcommand(parser: _SubParsersAction): + enable_parser = parser.add_parser("lfs-enable-largefiles", add_help=False) + enable_parser.add_argument("path", type=str, help="Local path to repository you want to configure.") + enable_parser.set_defaults(func=lambda args: LfsEnableCommand(args)) + + # Command will get called by git-lfs, do not call it directly. + upload_parser = parser.add_parser(LFS_MULTIPART_UPLOAD_COMMAND, add_help=False) + upload_parser.set_defaults(func=lambda args: LfsUploadCommand(args)) + + +class LfsEnableCommand: + def __init__(self, args): + self.args = args + + def run(self): + local_path = os.path.abspath(self.args.path) + if not os.path.isdir(local_path): + print("This does not look like a valid git repo.") + exit(1) + subprocess.run( + "git config lfs.customtransfer.multipart.path hf".split(), + check=True, + cwd=local_path, + ) + subprocess.run( + f"git config lfs.customtransfer.multipart.args {LFS_MULTIPART_UPLOAD_COMMAND}".split(), + check=True, + cwd=local_path, + ) + print("Local repo set up for largefiles") + + +def write_msg(msg: Dict): + """Write out the message in Line delimited JSON.""" + msg_str = json.dumps(msg) + "\n" + sys.stdout.write(msg_str) + sys.stdout.flush() + + +def read_msg() -> Optional[Dict]: + """Read Line delimited JSON from stdin.""" + msg = json.loads(sys.stdin.readline().strip()) + + if "terminate" in (msg.get("type"), msg.get("event")): + # terminate message received + return None + + if msg.get("event") not in ("download", "upload"): + logger.critical("Received unexpected message") + sys.exit(1) + + return msg + + +class LfsUploadCommand: + def __init__(self, args) -> None: + self.args = args + + def run(self) -> None: + # Immediately after invoking a custom transfer process, git-lfs + # sends initiation data to the process over stdin. + # This tells the process useful information about the configuration. + init_msg = json.loads(sys.stdin.readline().strip()) + if not (init_msg.get("event") == "init" and init_msg.get("operation") == "upload"): + write_msg({"error": {"code": 32, "message": "Wrong lfs init operation"}}) + sys.exit(1) + + # The transfer process should use the information it needs from the + # initiation structure, and also perform any one-off setup tasks it + # needs to do. It should then respond on stdout with a simple empty + # confirmation structure, as follows: + write_msg({}) + + # After the initiation exchange, git-lfs will send any number of + # transfer requests to the stdin of the transfer process, in a serial sequence. + while True: + msg = read_msg() + if msg is None: + # When all transfers have been processed, git-lfs will send + # a terminate event to the stdin of the transfer process. + # On receiving this message the transfer process should + # clean up and terminate. No response is expected. + sys.exit(0) + + oid = msg["oid"] + filepath = msg["path"] + completion_url = msg["action"]["href"] + header = msg["action"]["header"] + chunk_size = int(header.pop("chunk_size")) + presigned_urls: List[str] = list(header.values()) + + # Send a "started" progress event to allow other workers to start. + # Otherwise they're delayed until first "progress" event is reported, + # i.e. after the first 5GB by default (!) + write_msg( + { + "event": "progress", + "oid": oid, + "bytesSoFar": 1, + "bytesSinceLast": 0, + } + ) + + parts = [] + with open(filepath, "rb") as file: + for i, presigned_url in enumerate(presigned_urls): + with SliceFileObj( + file, + seek_from=i * chunk_size, + read_limit=chunk_size, + ) as data: + r = get_session().put(presigned_url, data=data) + hf_raise_for_status(r) + parts.append( + { + "etag": r.headers.get("etag"), + "partNumber": i + 1, + } + ) + # In order to support progress reporting while data is uploading / downloading, + # the transfer process should post messages to stdout + write_msg( + { + "event": "progress", + "oid": oid, + "bytesSoFar": (i + 1) * chunk_size, + "bytesSinceLast": chunk_size, + } + ) + # Not precise but that's ok. + + r = get_session().post( + completion_url, + json={ + "oid": oid, + "parts": parts, + }, + ) + hf_raise_for_status(r) + + write_msg({"event": "complete", "oid": oid}) diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/cli/repo.py b/venv/lib/python3.13/site-packages/huggingface_hub/cli/repo.py new file mode 100644 index 0000000000000000000000000000000000000000..ef0e3313580e3753a2617745e15762933229b15f --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/cli/repo.py @@ -0,0 +1,249 @@ +# Copyright 2025 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains commands to interact with repositories on the Hugging Face Hub. + +Usage: + # create a new dataset repo on the Hub + hf repo create my-cool-dataset --repo-type=dataset + + # create a private model repo on the Hub + hf repo create my-cool-model --private +""" + +import argparse +from argparse import _SubParsersAction +from typing import Optional + +from requests.exceptions import HTTPError + +from huggingface_hub.commands import BaseHuggingfaceCLICommand +from huggingface_hub.commands._cli_utils import ANSI +from huggingface_hub.constants import REPO_TYPES, SPACES_SDK_TYPES +from huggingface_hub.errors import HfHubHTTPError, RepositoryNotFoundError, RevisionNotFoundError +from huggingface_hub.hf_api import HfApi +from huggingface_hub.utils import logging + + +logger = logging.get_logger(__name__) + + +class RepoCommands(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction): + repo_parser = parser.add_parser("repo", help="Manage repos on the Hub.") + repo_subparsers = repo_parser.add_subparsers(help="huggingface.co repos related commands") + + # Show help if no subcommand is provided + repo_parser.set_defaults(func=lambda args: repo_parser.print_help()) + + # CREATE + repo_create_parser = repo_subparsers.add_parser("create", help="Create a new repo on huggingface.co") + repo_create_parser.add_argument( + "repo_id", + type=str, + help="The ID of the repo to create to (e.g. `username/repo-name`). The username is optional and will be set to your username if not provided.", + ) + repo_create_parser.add_argument( + "--repo-type", + type=str, + help='Optional: set to "dataset" or "space" if creating a dataset or space, default is model.', + ) + repo_create_parser.add_argument( + "--space_sdk", + type=str, + help='Optional: Hugging Face Spaces SDK type. Required when --type is set to "space".', + choices=SPACES_SDK_TYPES, + ) + repo_create_parser.add_argument( + "--private", + action="store_true", + help="Whether to create a private repository. Defaults to public unless the organization's default is private.", + ) + repo_create_parser.add_argument( + "--token", + type=str, + help="Hugging Face token. Will default to the locally saved token if not provided.", + ) + repo_create_parser.add_argument( + "--exist-ok", + action="store_true", + help="Do not raise an error if repo already exists.", + ) + repo_create_parser.add_argument( + "--resource-group-id", + type=str, + help="Resource group in which to create the repo. Resource groups is only available for Enterprise Hub organizations.", + ) + repo_create_parser.set_defaults(func=lambda args: RepoCreateCommand(args)) + + # TAG SUBCOMMANDS + repo_tag_parser = repo_subparsers.add_parser("tag", help="Manage tags for a repo on the Hub.") + tag_subparsers = repo_tag_parser.add_subparsers(help="Tag actions", dest="tag_action", required=True) + + # tag create + tag_create_parser = tag_subparsers.add_parser("create", help="Create a tag for a repo.") + tag_create_parser.add_argument( + "repo_id", type=str, help="The ID of the repo to tag (e.g. `username/repo-name`)." + ) + tag_create_parser.add_argument("tag", type=str, help="The name of the tag to create.") + tag_create_parser.add_argument("-m", "--message", type=str, help="The description of the tag to create.") + tag_create_parser.add_argument("--revision", type=str, help="The git revision to tag.") + tag_create_parser.add_argument( + "--token", type=str, help="A User Access Token generated from https://huggingface.co/settings/tokens." + ) + tag_create_parser.add_argument( + "--repo-type", + choices=["model", "dataset", "space"], + default="model", + help="Set the type of repository (model, dataset, or space).", + ) + tag_create_parser.set_defaults(func=lambda args: RepoTagCreateCommand(args)) + + # tag list + tag_list_parser = tag_subparsers.add_parser("list", help="List tags for a repo.") + tag_list_parser.add_argument( + "repo_id", type=str, help="The ID of the repo to list tags for (e.g. `username/repo-name`)." + ) + tag_list_parser.add_argument( + "--token", type=str, help="A User Access Token generated from https://huggingface.co/settings/tokens." + ) + tag_list_parser.add_argument( + "--repo-type", + choices=["model", "dataset", "space"], + default="model", + help="Set the type of repository (model, dataset, or space).", + ) + tag_list_parser.set_defaults(func=lambda args: RepoTagListCommand(args)) + + # tag delete + tag_delete_parser = tag_subparsers.add_parser("delete", help="Delete a tag from a repo.") + tag_delete_parser.add_argument( + "repo_id", type=str, help="The ID of the repo to delete the tag from (e.g. `username/repo-name`)." + ) + tag_delete_parser.add_argument("tag", type=str, help="The name of the tag to delete.") + tag_delete_parser.add_argument("-y", "--yes", action="store_true", help="Answer Yes to prompts automatically.") + tag_delete_parser.add_argument( + "--token", type=str, help="A User Access Token generated from https://huggingface.co/settings/tokens." + ) + tag_delete_parser.add_argument( + "--repo-type", + choices=["model", "dataset", "space"], + default="model", + help="Set the type of repository (model, dataset, or space).", + ) + tag_delete_parser.set_defaults(func=lambda args: RepoTagDeleteCommand(args)) + + +class RepoCreateCommand: + def __init__(self, args: argparse.Namespace): + self.repo_id: str = args.repo_id + self.repo_type: Optional[str] = args.repo_type + self.space_sdk: Optional[str] = args.space_sdk + self.private: bool = args.private + self.token: Optional[str] = args.token + self.exist_ok: bool = args.exist_ok + self.resource_group_id: Optional[str] = args.resource_group_id + self._api = HfApi() + + def run(self): + repo_url = self._api.create_repo( + repo_id=self.repo_id, + repo_type=self.repo_type, + private=self.private, + token=self.token, + exist_ok=self.exist_ok, + resource_group_id=self.resource_group_id, + space_sdk=self.space_sdk, + ) + print(f"Successfully created {ANSI.bold(repo_url.repo_id)} on the Hub.") + print(f"Your repo is now available at {ANSI.bold(repo_url)}") + + +class RepoTagCommand: + def __init__(self, args): + self.args = args + self.api = HfApi(token=getattr(args, "token", None)) + self.repo_id = args.repo_id + self.repo_type = getattr(args, "repo_type", "model") + if self.repo_type not in REPO_TYPES: + print("Invalid repo --repo-type") + exit(1) + + +class RepoTagCreateCommand(RepoTagCommand): + def run(self): + print( + f"You are about to create tag {ANSI.bold(str(self.args.tag))} on {self.repo_type} {ANSI.bold(self.repo_id)}" + ) + try: + self.api.create_tag( + repo_id=self.repo_id, + tag=self.args.tag, + tag_message=getattr(self.args, "message", None), + revision=getattr(self.args, "revision", None), + repo_type=self.repo_type, + ) + except RepositoryNotFoundError: + print(f"{self.repo_type.capitalize()} {ANSI.bold(self.repo_id)} not found.") + exit(1) + except RevisionNotFoundError: + print(f"Revision {ANSI.bold(str(getattr(self.args, 'revision', None)))} not found.") + exit(1) + except HfHubHTTPError as e: + if e.response.status_code == 409: + print(f"Tag {ANSI.bold(str(self.args.tag))} already exists on {ANSI.bold(self.repo_id)}") + exit(1) + raise e + print(f"Tag {ANSI.bold(str(self.args.tag))} created on {ANSI.bold(self.repo_id)}") + + +class RepoTagListCommand(RepoTagCommand): + def run(self): + try: + refs = self.api.list_repo_refs( + repo_id=self.repo_id, + repo_type=self.repo_type, + ) + except RepositoryNotFoundError: + print(f"{self.repo_type.capitalize()} {ANSI.bold(self.repo_id)} not found.") + exit(1) + except HTTPError as e: + print(e) + print(ANSI.red(e.response.text)) + exit(1) + if len(refs.tags) == 0: + print("No tags found") + exit(0) + print(f"Tags for {self.repo_type} {ANSI.bold(self.repo_id)}:") + for tag in refs.tags: + print(tag.name) + + +class RepoTagDeleteCommand(RepoTagCommand): + def run(self): + print(f"You are about to delete tag {ANSI.bold(self.args.tag)} on {self.repo_type} {ANSI.bold(self.repo_id)}") + if not getattr(self.args, "yes", False): + choice = input("Proceed? [Y/n] ").lower() + if choice not in ("", "y", "yes"): + print("Abort") + exit() + try: + self.api.delete_tag(repo_id=self.repo_id, tag=self.args.tag, repo_type=self.repo_type) + except RepositoryNotFoundError: + print(f"{self.repo_type.capitalize()} {ANSI.bold(self.repo_id)} not found.") + exit(1) + except RevisionNotFoundError: + print(f"Tag {ANSI.bold(self.args.tag)} not found on {ANSI.bold(self.repo_id)}") + exit(1) + print(f"Tag {ANSI.bold(self.args.tag)} deleted on {ANSI.bold(self.repo_id)}") diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/cli/repo_files.py b/venv/lib/python3.13/site-packages/huggingface_hub/cli/repo_files.py new file mode 100644 index 0000000000000000000000000000000000000000..403d3126e234c7561cde5fbf8f1d49d7e3271da8 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/cli/repo_files.py @@ -0,0 +1,128 @@ +# coding=utf-8 +# Copyright 2023-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains command to update or delete files in a repository using the CLI. + +Usage: + # delete all + hf repo-files delete "*" + + # delete single file + hf repo-files delete file.txt + + # delete single folder + hf repo-files delete folder/ + + # delete multiple + hf repo-files delete file.txt folder/ file2.txt + + # delete multiple patterns + hf repo-files delete file.txt "*.json" "folder/*.parquet" + + # delete from different revision / repo-type + hf repo-files delete file.txt --revision=refs/pr/1 --repo-type=dataset +""" + +from argparse import _SubParsersAction +from typing import List, Optional + +from huggingface_hub import logging +from huggingface_hub.commands import BaseHuggingfaceCLICommand +from huggingface_hub.hf_api import HfApi + + +logger = logging.get_logger(__name__) + + +class DeleteFilesSubCommand: + def __init__(self, args) -> None: + self.args = args + self.repo_id: str = args.repo_id + self.repo_type: Optional[str] = args.repo_type + self.revision: Optional[str] = args.revision + self.api: HfApi = HfApi(token=args.token, library_name="huggingface-cli") + self.patterns: List[str] = args.patterns + self.commit_message: Optional[str] = args.commit_message + self.commit_description: Optional[str] = args.commit_description + self.create_pr: bool = args.create_pr + self.token: Optional[str] = args.token + + def run(self) -> None: + logging.set_verbosity_info() + url = self.api.delete_files( + delete_patterns=self.patterns, + repo_id=self.repo_id, + repo_type=self.repo_type, + revision=self.revision, + commit_message=self.commit_message, + commit_description=self.commit_description, + create_pr=self.create_pr, + ) + print(f"Files correctly deleted from repo. Commit: {url}.") + logging.set_verbosity_warning() + + +class RepoFilesCommand(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction): + repo_files_parser = parser.add_parser("repo-files", help="Manage files in a repo on the Hub.") + repo_files_subparsers = repo_files_parser.add_subparsers( + help="Action to execute against the files.", + required=True, + ) + delete_subparser = repo_files_subparsers.add_parser( + "delete", + help="Delete files from a repo on the Hub", + ) + delete_subparser.set_defaults(func=lambda args: DeleteFilesSubCommand(args)) + delete_subparser.add_argument( + "repo_id", type=str, help="The ID of the repo to manage (e.g. `username/repo-name`)." + ) + delete_subparser.add_argument( + "patterns", + nargs="+", + type=str, + help="Glob patterns to match files to delete.", + ) + delete_subparser.add_argument( + "--repo-type", + choices=["model", "dataset", "space"], + default="model", + help="Type of the repo to upload to (e.g. `dataset`).", + ) + delete_subparser.add_argument( + "--revision", + type=str, + help=( + "An optional Git revision to push to. It can be a branch name " + "or a PR reference. If revision does not" + " exist and `--create-pr` is not set, a branch will be automatically created." + ), + ) + delete_subparser.add_argument( + "--commit-message", type=str, help="The summary / title / first line of the generated commit." + ) + delete_subparser.add_argument( + "--commit-description", type=str, help="The description of the generated commit." + ) + delete_subparser.add_argument( + "--create-pr", action="store_true", help="Whether to create a new Pull Request for these changes." + ) + delete_subparser.add_argument( + "--token", + type=str, + help="A User Access Token generated from https://huggingface.co/settings/tokens", + ) + + repo_files_parser.set_defaults(func=RepoFilesCommand) diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/cli/system.py b/venv/lib/python3.13/site-packages/huggingface_hub/cli/system.py new file mode 100644 index 0000000000000000000000000000000000000000..03650175e9b71e329755de5c86e5bbf50569d4b7 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/cli/system.py @@ -0,0 +1,52 @@ +# Copyright 2022 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains commands to print information about the environment and version. + +Usage: + hf env + hf version +""" + +from argparse import _SubParsersAction + +from huggingface_hub import __version__ + +from ..utils import dump_environment_info +from . import BaseHuggingfaceCLICommand + + +class EnvironmentCommand(BaseHuggingfaceCLICommand): + def __init__(self, args): + self.args = args + + @staticmethod + def register_subcommand(parser: _SubParsersAction): + env_parser = parser.add_parser("env", help="Print information about the environment.") + env_parser.set_defaults(func=EnvironmentCommand) + + def run(self) -> None: + dump_environment_info() + + +class VersionCommand(BaseHuggingfaceCLICommand): + def __init__(self, args): + self.args = args + + @staticmethod + def register_subcommand(parser: _SubParsersAction): + version_parser = parser.add_parser("version", help="Print information about the hf version.") + version_parser.set_defaults(func=VersionCommand) + + def run(self) -> None: + print(f"huggingface_hub version: {__version__}") diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/cli/upload.py b/venv/lib/python3.13/site-packages/huggingface_hub/cli/upload.py new file mode 100644 index 0000000000000000000000000000000000000000..0306bf9f5715fdc180dc4fa9819852388fca8b99 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/cli/upload.py @@ -0,0 +1,316 @@ +# coding=utf-8 +# Copyright 2023-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains command to upload a repo or file with the CLI. + +Usage: + # Upload file (implicit) + hf upload my-cool-model ./my-cool-model.safetensors + + # Upload file (explicit) + hf upload my-cool-model ./my-cool-model.safetensors model.safetensors + + # Upload directory (implicit). If `my-cool-model/` is a directory it will be uploaded, otherwise an exception is raised. + hf upload my-cool-model + + # Upload directory (explicit) + hf upload my-cool-model ./models/my-cool-model . + + # Upload filtered directory (example: tensorboard logs except for the last run) + hf upload my-cool-model ./model/training /logs --include "*.tfevents.*" --exclude "*20230905*" + + # Upload with wildcard + hf upload my-cool-model "./model/training/*.safetensors" + + # Upload private dataset + hf upload Wauplin/my-cool-dataset ./data . --repo-type=dataset --private + + # Upload with token + hf upload Wauplin/my-cool-model --token=hf_**** + + # Sync local Space with Hub (upload new files, delete removed files) + hf upload Wauplin/space-example --repo-type=space --exclude="/logs/*" --delete="*" --commit-message="Sync local Space with Hub" + + # Schedule commits every 30 minutes + hf upload Wauplin/my-cool-model --every=30 +""" + +import os +import time +import warnings +from argparse import Namespace, _SubParsersAction +from typing import List, Optional + +from huggingface_hub import logging +from huggingface_hub._commit_scheduler import CommitScheduler +from huggingface_hub.commands import BaseHuggingfaceCLICommand +from huggingface_hub.constants import HF_HUB_ENABLE_HF_TRANSFER +from huggingface_hub.errors import RevisionNotFoundError +from huggingface_hub.hf_api import HfApi +from huggingface_hub.utils import disable_progress_bars, enable_progress_bars +from huggingface_hub.utils._runtime import is_xet_available + + +logger = logging.get_logger(__name__) + + +class UploadCommand(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction): + upload_parser = parser.add_parser( + "upload", help="Upload a file or a folder to the Hub. Recommended for single-commit uploads." + ) + upload_parser.add_argument( + "repo_id", type=str, help="The ID of the repo to upload to (e.g. `username/repo-name`)." + ) + upload_parser.add_argument( + "local_path", + nargs="?", + help="Local path to the file or folder to upload. Wildcard patterns are supported. Defaults to current directory.", + ) + upload_parser.add_argument( + "path_in_repo", + nargs="?", + help="Path of the file or folder in the repo. Defaults to the relative path of the file or folder.", + ) + upload_parser.add_argument( + "--repo-type", + choices=["model", "dataset", "space"], + default="model", + help="Type of the repo to upload to (e.g. `dataset`).", + ) + upload_parser.add_argument( + "--revision", + type=str, + help=( + "An optional Git revision to push to. It can be a branch name or a PR reference. If revision does not" + " exist and `--create-pr` is not set, a branch will be automatically created." + ), + ) + upload_parser.add_argument( + "--private", + action="store_true", + help=( + "Whether to create a private repo if repo doesn't exist on the Hub. Ignored if the repo already" + " exists." + ), + ) + upload_parser.add_argument("--include", nargs="*", type=str, help="Glob patterns to match files to upload.") + upload_parser.add_argument( + "--exclude", nargs="*", type=str, help="Glob patterns to exclude from files to upload." + ) + upload_parser.add_argument( + "--delete", + nargs="*", + type=str, + help="Glob patterns for file to be deleted from the repo while committing.", + ) + upload_parser.add_argument( + "--commit-message", type=str, help="The summary / title / first line of the generated commit." + ) + upload_parser.add_argument("--commit-description", type=str, help="The description of the generated commit.") + upload_parser.add_argument( + "--create-pr", action="store_true", help="Whether to upload content as a new Pull Request." + ) + upload_parser.add_argument( + "--every", + type=float, + help="If set, a background job is scheduled to create commits every `every` minutes.", + ) + upload_parser.add_argument( + "--token", type=str, help="A User Access Token generated from https://huggingface.co/settings/tokens" + ) + upload_parser.add_argument( + "--quiet", + action="store_true", + help="If True, progress bars are disabled and only the path to the uploaded files is printed.", + ) + upload_parser.set_defaults(func=UploadCommand) + + def __init__(self, args: Namespace) -> None: + self.repo_id: str = args.repo_id + self.repo_type: Optional[str] = args.repo_type + self.revision: Optional[str] = args.revision + self.private: bool = args.private + + self.include: Optional[List[str]] = args.include + self.exclude: Optional[List[str]] = args.exclude + self.delete: Optional[List[str]] = args.delete + + self.commit_message: Optional[str] = args.commit_message + self.commit_description: Optional[str] = args.commit_description + self.create_pr: bool = args.create_pr + self.api: HfApi = HfApi(token=args.token, library_name="huggingface-cli") + self.quiet: bool = args.quiet # disable warnings and progress bars + + # Check `--every` is valid + if args.every is not None and args.every <= 0: + raise ValueError(f"`every` must be a positive value (got '{args.every}')") + self.every: Optional[float] = args.every + + # Resolve `local_path` and `path_in_repo` + repo_name: str = args.repo_id.split("/")[-1] # e.g. "Wauplin/my-cool-model" => "my-cool-model" + self.local_path: str + self.path_in_repo: str + + if args.local_path is not None and any(c in args.local_path for c in ["*", "?", "["]): + if args.include is not None: + raise ValueError("Cannot set `--include` when passing a `local_path` containing a wildcard.") + if args.path_in_repo is not None and args.path_in_repo != ".": + raise ValueError("Cannot set `path_in_repo` when passing a `local_path` containing a wildcard.") + self.local_path = "." + self.include = args.local_path + self.path_in_repo = "." + elif args.local_path is None and os.path.isfile(repo_name): + # Implicit case 1: user provided only a repo_id which happen to be a local file as well => upload it with same name + self.local_path = repo_name + self.path_in_repo = repo_name + elif args.local_path is None and os.path.isdir(repo_name): + # Implicit case 2: user provided only a repo_id which happen to be a local folder as well => upload it at root + self.local_path = repo_name + self.path_in_repo = "." + elif args.local_path is None: + # Implicit case 3: user provided only a repo_id that does not match a local file or folder + # => the user must explicitly provide a local_path => raise exception + raise ValueError(f"'{repo_name}' is not a local file or folder. Please set `local_path` explicitly.") + elif args.path_in_repo is None and os.path.isfile(args.local_path): + # Explicit local path to file, no path in repo => upload it at root with same name + self.local_path = args.local_path + self.path_in_repo = os.path.basename(args.local_path) + elif args.path_in_repo is None: + # Explicit local path to folder, no path in repo => upload at root + self.local_path = args.local_path + self.path_in_repo = "." + else: + # Finally, if both paths are explicit + self.local_path = args.local_path + self.path_in_repo = args.path_in_repo + + def run(self) -> None: + if self.quiet: + disable_progress_bars() + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + print(self._upload()) + enable_progress_bars() + else: + logging.set_verbosity_info() + print(self._upload()) + logging.set_verbosity_warning() + + def _upload(self) -> str: + if os.path.isfile(self.local_path): + if self.include is not None and len(self.include) > 0: + warnings.warn("Ignoring `--include` since a single file is uploaded.") + if self.exclude is not None and len(self.exclude) > 0: + warnings.warn("Ignoring `--exclude` since a single file is uploaded.") + if self.delete is not None and len(self.delete) > 0: + warnings.warn("Ignoring `--delete` since a single file is uploaded.") + + if not is_xet_available() and not HF_HUB_ENABLE_HF_TRANSFER: + logger.info( + "Consider using `hf_transfer` for faster uploads. This solution comes with some limitations. See" + " https://huggingface.co/docs/huggingface_hub/hf_transfer for more details." + ) + + # Schedule commits if `every` is set + if self.every is not None: + if os.path.isfile(self.local_path): + # If file => watch entire folder + use allow_patterns + folder_path = os.path.dirname(self.local_path) + path_in_repo = ( + self.path_in_repo[: -len(self.local_path)] # remove filename from path_in_repo + if self.path_in_repo.endswith(self.local_path) + else self.path_in_repo + ) + allow_patterns = [self.local_path] + ignore_patterns = [] + else: + folder_path = self.local_path + path_in_repo = self.path_in_repo + allow_patterns = self.include or [] + ignore_patterns = self.exclude or [] + if self.delete is not None and len(self.delete) > 0: + warnings.warn("Ignoring `--delete` when uploading with scheduled commits.") + + scheduler = CommitScheduler( + folder_path=folder_path, + repo_id=self.repo_id, + repo_type=self.repo_type, + revision=self.revision, + allow_patterns=allow_patterns, + ignore_patterns=ignore_patterns, + path_in_repo=path_in_repo, + private=self.private, + every=self.every, + hf_api=self.api, + ) + print(f"Scheduling commits every {self.every} minutes to {scheduler.repo_id}.") + try: # Block main thread until KeyboardInterrupt + while True: + time.sleep(100) + except KeyboardInterrupt: + scheduler.stop() + return "Stopped scheduled commits." + + # Otherwise, create repo and proceed with the upload + if not os.path.isfile(self.local_path) and not os.path.isdir(self.local_path): + raise FileNotFoundError(f"No such file or directory: '{self.local_path}'.") + repo_id = self.api.create_repo( + repo_id=self.repo_id, + repo_type=self.repo_type, + exist_ok=True, + private=self.private, + space_sdk="gradio" if self.repo_type == "space" else None, + # ^ We don't want it to fail when uploading to a Space => let's set Gradio by default. + # ^ I'd rather not add CLI args to set it explicitly as we already have `hf repo create` for that. + ).repo_id + + # Check if branch already exists and if not, create it + if self.revision is not None and not self.create_pr: + try: + self.api.repo_info(repo_id=repo_id, repo_type=self.repo_type, revision=self.revision) + except RevisionNotFoundError: + logger.info(f"Branch '{self.revision}' not found. Creating it...") + self.api.create_branch(repo_id=repo_id, repo_type=self.repo_type, branch=self.revision, exist_ok=True) + # ^ `exist_ok=True` to avoid race concurrency issues + + # File-based upload + if os.path.isfile(self.local_path): + return self.api.upload_file( + path_or_fileobj=self.local_path, + path_in_repo=self.path_in_repo, + repo_id=repo_id, + repo_type=self.repo_type, + revision=self.revision, + commit_message=self.commit_message, + commit_description=self.commit_description, + create_pr=self.create_pr, + ) + + # Folder-based upload + else: + return self.api.upload_folder( + folder_path=self.local_path, + path_in_repo=self.path_in_repo, + repo_id=repo_id, + repo_type=self.repo_type, + revision=self.revision, + commit_message=self.commit_message, + commit_description=self.commit_description, + create_pr=self.create_pr, + allow_patterns=self.include, + ignore_patterns=self.exclude, + delete_patterns=self.delete, + ) diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/cli/upload_large_folder.py b/venv/lib/python3.13/site-packages/huggingface_hub/cli/upload_large_folder.py new file mode 100644 index 0000000000000000000000000000000000000000..675c9ffe3dcd70242a9acd7837c6c2f00d8836df --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/cli/upload_large_folder.py @@ -0,0 +1,132 @@ +# coding=utf-8 +# Copyright 2023-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains command to upload a large folder with the CLI.""" + +import os +from argparse import Namespace, _SubParsersAction +from typing import List, Optional + +from huggingface_hub import logging +from huggingface_hub.commands import BaseHuggingfaceCLICommand +from huggingface_hub.hf_api import HfApi +from huggingface_hub.utils import disable_progress_bars + +from ._cli_utils import ANSI + + +logger = logging.get_logger(__name__) + + +class UploadLargeFolderCommand(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction): + subparser = parser.add_parser( + "upload-large-folder", + help="Upload a large folder to the Hub. Recommended for resumable uploads.", + ) + subparser.add_argument( + "repo_id", type=str, help="The ID of the repo to upload to (e.g. `username/repo-name`)." + ) + subparser.add_argument("local_path", type=str, help="Local path to the file or folder to upload.") + subparser.add_argument( + "--repo-type", + choices=["model", "dataset", "space"], + help="Type of the repo to upload to (e.g. `dataset`).", + ) + subparser.add_argument( + "--revision", + type=str, + help=("An optional Git revision to push to. It can be a branch name or a PR reference."), + ) + subparser.add_argument( + "--private", + action="store_true", + help=( + "Whether to create a private repo if repo doesn't exist on the Hub. Ignored if the repo already exists." + ), + ) + subparser.add_argument("--include", nargs="*", type=str, help="Glob patterns to match files to upload.") + subparser.add_argument("--exclude", nargs="*", type=str, help="Glob patterns to exclude from files to upload.") + subparser.add_argument( + "--token", type=str, help="A User Access Token generated from https://huggingface.co/settings/tokens" + ) + subparser.add_argument( + "--num-workers", type=int, help="Number of workers to use to hash, upload and commit files." + ) + subparser.add_argument("--no-report", action="store_true", help="Whether to disable regular status report.") + subparser.add_argument("--no-bars", action="store_true", help="Whether to disable progress bars.") + subparser.set_defaults(func=UploadLargeFolderCommand) + + def __init__(self, args: Namespace) -> None: + self.repo_id: str = args.repo_id + self.local_path: str = args.local_path + self.repo_type: str = args.repo_type + self.revision: Optional[str] = args.revision + self.private: bool = args.private + + self.include: Optional[List[str]] = args.include + self.exclude: Optional[List[str]] = args.exclude + + self.api: HfApi = HfApi(token=args.token, library_name="huggingface-cli") + + self.num_workers: Optional[int] = args.num_workers + self.no_report: bool = args.no_report + self.no_bars: bool = args.no_bars + + if not os.path.isdir(self.local_path): + raise ValueError("Large upload is only supported for folders.") + + def run(self) -> None: + logging.set_verbosity_info() + + print( + ANSI.yellow( + "You are about to upload a large folder to the Hub using `hf upload-large-folder`. " + "This is a new feature so feedback is very welcome!\n" + "\n" + "A few things to keep in mind:\n" + " - Repository limits still apply: https://huggingface.co/docs/hub/repositories-recommendations\n" + " - Do not start several processes in parallel.\n" + " - You can interrupt and resume the process at any time. " + "The script will pick up where it left off except for partially uploaded files that would have to be entirely reuploaded.\n" + " - Do not upload the same folder to several repositories. If you need to do so, you must delete the `./.cache/huggingface/` folder first.\n" + "\n" + f"Some temporary metadata will be stored under `{self.local_path}/.cache/huggingface`.\n" + " - You must not modify those files manually.\n" + " - You must not delete the `./.cache/huggingface/` folder while a process is running.\n" + " - You can delete the `./.cache/huggingface/` folder to reinitialize the upload state when process is not running. Files will have to be hashed and preuploaded again, except for already committed files.\n" + "\n" + "If the process output is too verbose, you can disable the progress bars with `--no-bars`. " + "You can also entirely disable the status report with `--no-report`.\n" + "\n" + "For more details, run `hf upload-large-folder --help` or check the documentation at " + "https://huggingface.co/docs/huggingface_hub/guides/upload#upload-a-large-folder." + ) + ) + + if self.no_bars: + disable_progress_bars() + + self.api.upload_large_folder( + repo_id=self.repo_id, + folder_path=self.local_path, + repo_type=self.repo_type, + revision=self.revision, + private=self.private, + allow_patterns=self.include, + ignore_patterns=self.exclude, + num_workers=self.num_workers, + print_report=not self.no_report, + ) diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/commands/__init__.py b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..49d088214505b9604964ab142e7f8a5b38ccd5ef --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__init__.py @@ -0,0 +1,27 @@ +# Copyright 2020 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from abc import ABC, abstractmethod +from argparse import _SubParsersAction + + +class BaseHuggingfaceCLICommand(ABC): + @staticmethod + @abstractmethod + def register_subcommand(parser: _SubParsersAction): + raise NotImplementedError() + + @abstractmethod + def run(self): + raise NotImplementedError() diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4925e4d7b6035a0fc58c4471695e411e8484b2c9 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/_cli_utils.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/_cli_utils.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9ea6bf81c706ca74093d8ff787c3335d21048d44 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/_cli_utils.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/delete_cache.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/delete_cache.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7e2726f523d398e7c818eabcb83841694400f706 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/delete_cache.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/download.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/download.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9c5183deb09bbadbc0063ae8305bc0f1efc3e0cc Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/download.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/env.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/env.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..535962c304029ccc16ccf4104da4111ccd1026ee Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/env.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/huggingface_cli.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/huggingface_cli.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4b89ac62bd29424d103e20836c84fd180beeca98 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/huggingface_cli.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/lfs.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/lfs.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ca4c2ab21bbe5d19a07eef5f2acc30b2186cb82e Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/lfs.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/repo.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/repo.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9e933e3ea644dc74f52750556a206425657f6ed8 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/repo.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/repo_files.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/repo_files.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..71852fb6df7406e8a7e485703df17cfc398246f4 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/repo_files.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/scan_cache.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/scan_cache.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cbc807e89879ca60927172a33fde16c705afbea9 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/scan_cache.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/tag.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/tag.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9c5a30dcdb723811994e425732858bba9d66bd20 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/tag.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/upload.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/upload.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..facf744f8b2552872c7f800c4a9add168a0d94e3 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/upload.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/upload_large_folder.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/upload_large_folder.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c1760942211c30965dedafebbc19ce1b74f73053 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/upload_large_folder.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/user.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/user.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..22c55b80a13d8c1ce8e806fbe3b4f604b490f19d Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/user.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/version.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/version.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6cfe0e0522514374e49cfb9a7c8fd505887fc05f Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/commands/__pycache__/version.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/commands/_cli_utils.py b/venv/lib/python3.13/site-packages/huggingface_hub/commands/_cli_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..bf4a1c0373b4d4bb71a3f4e8ea39da5a01cc79a7 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/commands/_cli_utils.py @@ -0,0 +1,74 @@ +# Copyright 2022 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains a utility for good-looking prints.""" + +import os +from typing import List, Union + + +class ANSI: + """ + Helper for en.wikipedia.org/wiki/ANSI_escape_code + """ + + _bold = "\u001b[1m" + _gray = "\u001b[90m" + _red = "\u001b[31m" + _reset = "\u001b[0m" + _yellow = "\u001b[33m" + + @classmethod + def bold(cls, s: str) -> str: + return cls._format(s, cls._bold) + + @classmethod + def gray(cls, s: str) -> str: + return cls._format(s, cls._gray) + + @classmethod + def red(cls, s: str) -> str: + return cls._format(s, cls._bold + cls._red) + + @classmethod + def yellow(cls, s: str) -> str: + return cls._format(s, cls._yellow) + + @classmethod + def _format(cls, s: str, code: str) -> str: + if os.environ.get("NO_COLOR"): + # See https://no-color.org/ + return s + return f"{code}{s}{cls._reset}" + + +def tabulate(rows: List[List[Union[str, int]]], headers: List[str]) -> str: + """ + Inspired by: + + - stackoverflow.com/a/8356620/593036 + - stackoverflow.com/questions/9535954/printing-lists-as-tabular-data + """ + col_widths = [max(len(str(x)) for x in col) for col in zip(*rows, headers)] + row_format = ("{{:{}}} " * len(headers)).format(*col_widths) + lines = [] + lines.append(row_format.format(*headers)) + lines.append(row_format.format(*["-" * w for w in col_widths])) + for row in rows: + lines.append(row_format.format(*row)) + return "\n".join(lines) + + +def show_deprecation_warning(old_command: str, new_command: str): + """Show a yellow warning about deprecated CLI command.""" + print(ANSI.yellow(f"⚠️ Warning: '{old_command}' is deprecated. Use '{new_command}' instead.")) diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/commands/delete_cache.py b/venv/lib/python3.13/site-packages/huggingface_hub/commands/delete_cache.py new file mode 100644 index 0000000000000000000000000000000000000000..78ea1179678371807b3686b8acf17b9f0997035f --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/commands/delete_cache.py @@ -0,0 +1,476 @@ +# coding=utf-8 +# Copyright 2022-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains command to delete some revisions from the HF cache directory. + +Usage: + huggingface-cli delete-cache + huggingface-cli delete-cache --disable-tui + huggingface-cli delete-cache --dir ~/.cache/huggingface/hub + huggingface-cli delete-cache --sort=size + +NOTE: + This command is based on `InquirerPy` to build the multiselect menu in the terminal. + This dependency has to be installed with `pip install "huggingface_hub[cli]"`. Since + we want to avoid as much as possible cross-platform issues, I chose a library that + is built on top of `python-prompt-toolkit` which seems to be a reference in terminal + GUI (actively maintained on both Unix and Windows, 7.9k stars). + + For the moment, the TUI feature is in beta. + + See: + - https://github.com/kazhala/InquirerPy + - https://inquirerpy.readthedocs.io/en/latest/ + - https://github.com/prompt-toolkit/python-prompt-toolkit + + Other solutions could have been: + - `simple_term_menu`: would be good as well for our use case but some issues suggest + that Windows is less supported. + See: https://github.com/IngoMeyer441/simple-term-menu + - `PyInquirer`: very similar to `InquirerPy` but older and not maintained anymore. + In particular, no support of Python3.10. + See: https://github.com/CITGuru/PyInquirer + - `pick` (or `pickpack`): easy to use and flexible but built on top of Python's + standard library `curses` that is specific to Unix (not implemented on Windows). + See https://github.com/wong2/pick and https://github.com/anafvana/pickpack. + - `inquirer`: lot of traction (700 stars) but explicitly states "experimental + support of Windows". Not built on top of `python-prompt-toolkit`. + See https://github.com/magmax/python-inquirer + +TODO: add support for `huggingface-cli delete-cache aaaaaa bbbbbb cccccc (...)` ? +TODO: add "--keep-last" arg to delete revisions that are not on `main` ref +TODO: add "--filter" arg to filter repositories by name ? +TODO: add "--limit" arg to limit to X repos ? +TODO: add "-y" arg for immediate deletion ? +See discussions in https://github.com/huggingface/huggingface_hub/issues/1025. +""" + +import os +from argparse import Namespace, _SubParsersAction +from functools import wraps +from tempfile import mkstemp +from typing import Any, Callable, Iterable, List, Literal, Optional, Union + +from ..utils import CachedRepoInfo, CachedRevisionInfo, HFCacheInfo, scan_cache_dir +from . import BaseHuggingfaceCLICommand +from ._cli_utils import ANSI, show_deprecation_warning + + +try: + from InquirerPy import inquirer + from InquirerPy.base.control import Choice + from InquirerPy.separator import Separator + + _inquirer_py_available = True +except ImportError: + _inquirer_py_available = False + +SortingOption_T = Literal["alphabetical", "lastUpdated", "lastUsed", "size"] + + +def require_inquirer_py(fn: Callable) -> Callable: + """Decorator to flag methods that require `InquirerPy`.""" + + # TODO: refactor this + imports in a unified pattern across codebase + @wraps(fn) + def _inner(*args, **kwargs): + if not _inquirer_py_available: + raise ImportError( + "The `delete-cache` command requires extra dependencies to work with" + ' the TUI.\nPlease run `pip install "huggingface_hub[cli]"` to install' + " them.\nOtherwise, disable TUI using the `--disable-tui` flag." + ) + + return fn(*args, **kwargs) + + return _inner + + +# Possibility for the user to cancel deletion +_CANCEL_DELETION_STR = "CANCEL_DELETION" + + +class DeleteCacheCommand(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction): + delete_cache_parser = parser.add_parser("delete-cache", help="Delete revisions from the cache directory.") + + delete_cache_parser.add_argument( + "--dir", + type=str, + default=None, + help="cache directory (optional). Default to the default HuggingFace cache.", + ) + + delete_cache_parser.add_argument( + "--disable-tui", + action="store_true", + help=( + "Disable Terminal User Interface (TUI) mode. Useful if your" + " platform/terminal doesn't support the multiselect menu." + ), + ) + + delete_cache_parser.add_argument( + "--sort", + nargs="?", + choices=["alphabetical", "lastUpdated", "lastUsed", "size"], + help=( + "Sort repositories by the specified criteria. Options: " + "'alphabetical' (A-Z), " + "'lastUpdated' (newest first), " + "'lastUsed' (most recent first), " + "'size' (largest first)." + ), + ) + + delete_cache_parser.set_defaults(func=DeleteCacheCommand) + + def __init__(self, args: Namespace) -> None: + self.cache_dir: Optional[str] = args.dir + self.disable_tui: bool = args.disable_tui + self.sort_by: Optional[SortingOption_T] = args.sort + + def run(self): + """Run `delete-cache` command with or without TUI.""" + show_deprecation_warning("huggingface-cli delete-cache", "hf cache delete") + + # Scan cache directory + hf_cache_info = scan_cache_dir(self.cache_dir) + + # Manual review from the user + if self.disable_tui: + selected_hashes = _manual_review_no_tui(hf_cache_info, preselected=[], sort_by=self.sort_by) + else: + selected_hashes = _manual_review_tui(hf_cache_info, preselected=[], sort_by=self.sort_by) + + # If deletion is not cancelled + if len(selected_hashes) > 0 and _CANCEL_DELETION_STR not in selected_hashes: + confirm_message = _get_expectations_str(hf_cache_info, selected_hashes) + " Confirm deletion ?" + + # Confirm deletion + if self.disable_tui: + confirmed = _ask_for_confirmation_no_tui(confirm_message) + else: + confirmed = _ask_for_confirmation_tui(confirm_message) + + # Deletion is confirmed + if confirmed: + strategy = hf_cache_info.delete_revisions(*selected_hashes) + print("Start deletion.") + strategy.execute() + print( + f"Done. Deleted {len(strategy.repos)} repo(s) and" + f" {len(strategy.snapshots)} revision(s) for a total of" + f" {strategy.expected_freed_size_str}." + ) + return + + # Deletion is cancelled + print("Deletion is cancelled. Do nothing.") + + +def _get_repo_sorting_key(repo: CachedRepoInfo, sort_by: Optional[SortingOption_T] = None): + if sort_by == "alphabetical": + return (repo.repo_type, repo.repo_id.lower()) # by type then name + elif sort_by == "lastUpdated": + return -max(rev.last_modified for rev in repo.revisions) # newest first + elif sort_by == "lastUsed": + return -repo.last_accessed # most recently used first + elif sort_by == "size": + return -repo.size_on_disk # largest first + else: + return (repo.repo_type, repo.repo_id) # default stable order + + +@require_inquirer_py +def _manual_review_tui( + hf_cache_info: HFCacheInfo, + preselected: List[str], + sort_by: Optional[SortingOption_T] = None, +) -> List[str]: + """Ask the user for a manual review of the revisions to delete. + + Displays a multi-select menu in the terminal (TUI). + """ + # Define multiselect list + choices = _get_tui_choices_from_scan( + repos=hf_cache_info.repos, + preselected=preselected, + sort_by=sort_by, + ) + checkbox = inquirer.checkbox( + message="Select revisions to delete:", + choices=choices, # List of revisions with some pre-selection + cycle=False, # No loop between top and bottom + height=100, # Large list if possible + # We use the instruction to display to the user the expected effect of the + # deletion. + instruction=_get_expectations_str( + hf_cache_info, + selected_hashes=[c.value for c in choices if isinstance(c, Choice) and c.enabled], + ), + # We use the long instruction to should keybindings instructions to the user + long_instruction="Press to select, to validate and to quit without modification.", + # Message that is displayed once the user validates its selection. + transformer=lambda result: f"{len(result)} revision(s) selected.", + ) + + # Add a callback to update the information line when a revision is + # selected/unselected + def _update_expectations(_) -> None: + # Hacky way to dynamically set an instruction message to the checkbox when + # a revision hash is selected/unselected. + checkbox._instruction = _get_expectations_str( + hf_cache_info, + selected_hashes=[choice["value"] for choice in checkbox.content_control.choices if choice["enabled"]], + ) + + checkbox.kb_func_lookup["toggle"].append({"func": _update_expectations}) + + # Finally display the form to the user. + try: + return checkbox.execute() + except KeyboardInterrupt: + return [] # Quit without deletion + + +@require_inquirer_py +def _ask_for_confirmation_tui(message: str, default: bool = True) -> bool: + """Ask for confirmation using Inquirer.""" + return inquirer.confirm(message, default=default).execute() + + +def _get_tui_choices_from_scan( + repos: Iterable[CachedRepoInfo], + preselected: List[str], + sort_by: Optional[SortingOption_T] = None, +) -> List: + """Build a list of choices from the scanned repos. + + Args: + repos (*Iterable[`CachedRepoInfo`]*): + List of scanned repos on which we want to delete revisions. + preselected (*List[`str`]*): + List of revision hashes that will be preselected. + sort_by (*Optional[SortingOption_T]*): + Sorting direction. Choices: "alphabetical", "lastUpdated", "lastUsed", "size". + + Return: + The list of choices to pass to `inquirer.checkbox`. + """ + choices: List[Union[Choice, Separator]] = [] + + # First choice is to cancel the deletion + choices.append( + Choice( + _CANCEL_DELETION_STR, + name="None of the following (if selected, nothing will be deleted).", + enabled=False, + ) + ) + + # Sort repos based on specified criteria + sorted_repos = sorted(repos, key=lambda repo: _get_repo_sorting_key(repo, sort_by)) + + for repo in sorted_repos: + # Repo as separator + choices.append( + Separator( + f"\n{repo.repo_type.capitalize()} {repo.repo_id} ({repo.size_on_disk_str}," + f" used {repo.last_accessed_str})" + ) + ) + for revision in sorted(repo.revisions, key=_revision_sorting_order): + # Revision as choice + choices.append( + Choice( + revision.commit_hash, + name=( + f"{revision.commit_hash[:8]}:" + f" {', '.join(sorted(revision.refs)) or '(detached)'} #" + f" modified {revision.last_modified_str}" + ), + enabled=revision.commit_hash in preselected, + ) + ) + + # Return choices + return choices + + +def _manual_review_no_tui( + hf_cache_info: HFCacheInfo, + preselected: List[str], + sort_by: Optional[SortingOption_T] = None, +) -> List[str]: + """Ask the user for a manual review of the revisions to delete. + + Used when TUI is disabled. Manual review happens in a separate tmp file that the + user can manually edit. + """ + # 1. Generate temporary file with delete commands. + fd, tmp_path = mkstemp(suffix=".txt") # suffix to make it easier to find by editors + os.close(fd) + + lines = [] + + sorted_repos = sorted(hf_cache_info.repos, key=lambda repo: _get_repo_sorting_key(repo, sort_by)) + + for repo in sorted_repos: + lines.append( + f"\n# {repo.repo_type.capitalize()} {repo.repo_id} ({repo.size_on_disk_str}," + f" used {repo.last_accessed_str})" + ) + for revision in sorted(repo.revisions, key=_revision_sorting_order): + lines.append( + # Deselect by prepending a '#' + f"{'' if revision.commit_hash in preselected else '#'} " + f" {revision.commit_hash} # Refs:" + # Print `refs` as comment on same line + f" {', '.join(sorted(revision.refs)) or '(detached)'} # modified" + # Print `last_modified` as comment on same line + f" {revision.last_modified_str}" + ) + + with open(tmp_path, "w") as f: + f.write(_MANUAL_REVIEW_NO_TUI_INSTRUCTIONS) + f.write("\n".join(lines)) + + # 2. Prompt instructions to user. + instructions = f""" + TUI is disabled. In order to select which revisions you want to delete, please edit + the following file using the text editor of your choice. Instructions for manual + editing are located at the beginning of the file. Edit the file, save it and confirm + to continue. + File to edit: {ANSI.bold(tmp_path)} + """ + print("\n".join(line.strip() for line in instructions.strip().split("\n"))) + + # 3. Wait for user confirmation. + while True: + selected_hashes = _read_manual_review_tmp_file(tmp_path) + if _ask_for_confirmation_no_tui( + _get_expectations_str(hf_cache_info, selected_hashes) + " Continue ?", + default=False, + ): + break + + # 4. Return selected_hashes sorted to maintain stable order + os.remove(tmp_path) + return sorted(selected_hashes) # Sort to maintain stable order + + +def _ask_for_confirmation_no_tui(message: str, default: bool = True) -> bool: + """Ask for confirmation using pure-python.""" + YES = ("y", "yes", "1") + NO = ("n", "no", "0") + DEFAULT = "" + ALL = YES + NO + (DEFAULT,) + full_message = message + (" (Y/n) " if default else " (y/N) ") + while True: + answer = input(full_message).lower() + if answer == DEFAULT: + return default + if answer in YES: + return True + if answer in NO: + return False + print(f"Invalid input. Must be one of {ALL}") + + +def _get_expectations_str(hf_cache_info: HFCacheInfo, selected_hashes: List[str]) -> str: + """Format a string to display to the user how much space would be saved. + + Example: + ``` + >>> _get_expectations_str(hf_cache_info, selected_hashes) + '7 revisions selected counting for 4.3G.' + ``` + """ + if _CANCEL_DELETION_STR in selected_hashes: + return "Nothing will be deleted." + strategy = hf_cache_info.delete_revisions(*selected_hashes) + return f"{len(selected_hashes)} revisions selected counting for {strategy.expected_freed_size_str}." + + +def _read_manual_review_tmp_file(tmp_path: str) -> List[str]: + """Read the manually reviewed instruction file and return a list of revision hash. + + Example: + ```txt + # This is the tmp file content + ### + + # Commented out line + 123456789 # revision hash + + # Something else + # a_newer_hash # 2 days ago + an_older_hash # 3 days ago + ``` + + ```py + >>> _read_manual_review_tmp_file(tmp_path) + ['123456789', 'an_older_hash'] + ``` + """ + with open(tmp_path) as f: + content = f.read() + + # Split lines + lines = [line.strip() for line in content.split("\n")] + + # Filter commented lines + selected_lines = [line for line in lines if not line.startswith("#")] + + # Select only before comment + selected_hashes = [line.split("#")[0].strip() for line in selected_lines] + + # Return revision hashes + return [hash for hash in selected_hashes if len(hash) > 0] + + +_MANUAL_REVIEW_NO_TUI_INSTRUCTIONS = f""" +# INSTRUCTIONS +# ------------ +# This is a temporary file created by running `huggingface-cli delete-cache` with the +# `--disable-tui` option. It contains a set of revisions that can be deleted from your +# local cache directory. +# +# Please manually review the revisions you want to delete: +# - Revision hashes can be commented out with '#'. +# - Only non-commented revisions in this file will be deleted. +# - Revision hashes that are removed from this file are ignored as well. +# - If `{_CANCEL_DELETION_STR}` line is uncommented, the all cache deletion is cancelled and +# no changes will be applied. +# +# Once you've manually reviewed this file, please confirm deletion in the terminal. This +# file will be automatically removed once done. +# ------------ + +# KILL SWITCH +# ------------ +# Un-comment following line to completely cancel the deletion process +# {_CANCEL_DELETION_STR} +# ------------ + +# REVISIONS +# ------------ +""".strip() + + +def _revision_sorting_order(revision: CachedRevisionInfo) -> Any: + # Sort by last modified (oldest first) + return revision.last_modified diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/commands/download.py b/venv/lib/python3.13/site-packages/huggingface_hub/commands/download.py new file mode 100644 index 0000000000000000000000000000000000000000..0dd2c1070ead01f9ad6855de3929928d268279c2 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/commands/download.py @@ -0,0 +1,204 @@ +# coding=utf-8 +# Copyright 2023-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains command to download files from the Hub with the CLI. + +Usage: + huggingface-cli download --help + + # Download file + huggingface-cli download gpt2 config.json + + # Download entire repo + huggingface-cli download fffiloni/zeroscope --repo-type=space --revision=refs/pr/78 + + # Download repo with filters + huggingface-cli download gpt2 --include="*.safetensors" + + # Download with token + huggingface-cli download Wauplin/private-model --token=hf_*** + + # Download quietly (no progress bar, no warnings, only the returned path) + huggingface-cli download gpt2 config.json --quiet + + # Download to local dir + huggingface-cli download gpt2 --local-dir=./models/gpt2 +""" + +import warnings +from argparse import Namespace, _SubParsersAction +from typing import List, Optional + +from huggingface_hub import logging +from huggingface_hub._snapshot_download import snapshot_download +from huggingface_hub.commands import BaseHuggingfaceCLICommand +from huggingface_hub.file_download import hf_hub_download +from huggingface_hub.utils import disable_progress_bars, enable_progress_bars + +from ._cli_utils import show_deprecation_warning + + +logger = logging.get_logger(__name__) + + +class DownloadCommand(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction): + download_parser = parser.add_parser("download", help="Download files from the Hub") + download_parser.add_argument( + "repo_id", type=str, help="ID of the repo to download from (e.g. `username/repo-name`)." + ) + download_parser.add_argument( + "filenames", type=str, nargs="*", help="Files to download (e.g. `config.json`, `data/metadata.jsonl`)." + ) + download_parser.add_argument( + "--repo-type", + choices=["model", "dataset", "space"], + default="model", + help="Type of repo to download from (defaults to 'model').", + ) + download_parser.add_argument( + "--revision", + type=str, + help="An optional Git revision id which can be a branch name, a tag, or a commit hash.", + ) + download_parser.add_argument( + "--include", nargs="*", type=str, help="Glob patterns to match files to download." + ) + download_parser.add_argument( + "--exclude", nargs="*", type=str, help="Glob patterns to exclude from files to download." + ) + download_parser.add_argument( + "--cache-dir", type=str, help="Path to the directory where to save the downloaded files." + ) + download_parser.add_argument( + "--local-dir", + type=str, + help=( + "If set, the downloaded file will be placed under this directory. Check out" + " https://huggingface.co/docs/huggingface_hub/guides/download#download-files-to-local-folder for more" + " details." + ), + ) + download_parser.add_argument( + "--local-dir-use-symlinks", + choices=["auto", "True", "False"], + help=("Deprecated and ignored. Downloading to a local directory does not use symlinks anymore."), + ) + download_parser.add_argument( + "--force-download", + action="store_true", + help="If True, the files will be downloaded even if they are already cached.", + ) + download_parser.add_argument( + "--resume-download", + action="store_true", + help="Deprecated and ignored. Downloading a file to local dir always attempts to resume previously interrupted downloads (unless hf-transfer is enabled).", + ) + download_parser.add_argument( + "--token", type=str, help="A User Access Token generated from https://huggingface.co/settings/tokens" + ) + download_parser.add_argument( + "--quiet", + action="store_true", + help="If True, progress bars are disabled and only the path to the download files is printed.", + ) + download_parser.add_argument( + "--max-workers", + type=int, + default=8, + help="Maximum number of workers to use for downloading files. Default is 8.", + ) + download_parser.set_defaults(func=DownloadCommand) + + def __init__(self, args: Namespace) -> None: + self.token = args.token + self.repo_id: str = args.repo_id + self.filenames: List[str] = args.filenames + self.repo_type: str = args.repo_type + self.revision: Optional[str] = args.revision + self.include: Optional[List[str]] = args.include + self.exclude: Optional[List[str]] = args.exclude + self.cache_dir: Optional[str] = args.cache_dir + self.local_dir: Optional[str] = args.local_dir + self.force_download: bool = args.force_download + self.resume_download: Optional[bool] = args.resume_download or None + self.quiet: bool = args.quiet + self.max_workers: int = args.max_workers + + if args.local_dir_use_symlinks is not None: + warnings.warn( + "Ignoring --local-dir-use-symlinks. Downloading to a local directory does not use symlinks anymore.", + FutureWarning, + ) + + def run(self) -> None: + show_deprecation_warning("huggingface-cli download", "hf download") + + if self.quiet: + disable_progress_bars() + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + print(self._download()) # Print path to downloaded files + enable_progress_bars() + else: + logging.set_verbosity_info() + print(self._download()) # Print path to downloaded files + logging.set_verbosity_warning() + + def _download(self) -> str: + # Warn user if patterns are ignored + if len(self.filenames) > 0: + if self.include is not None and len(self.include) > 0: + warnings.warn("Ignoring `--include` since filenames have being explicitly set.") + if self.exclude is not None and len(self.exclude) > 0: + warnings.warn("Ignoring `--exclude` since filenames have being explicitly set.") + + # Single file to download: use `hf_hub_download` + if len(self.filenames) == 1: + return hf_hub_download( + repo_id=self.repo_id, + repo_type=self.repo_type, + revision=self.revision, + filename=self.filenames[0], + cache_dir=self.cache_dir, + resume_download=self.resume_download, + force_download=self.force_download, + token=self.token, + local_dir=self.local_dir, + library_name="huggingface-cli", + ) + + # Otherwise: use `snapshot_download` to ensure all files comes from same revision + elif len(self.filenames) == 0: + allow_patterns = self.include + ignore_patterns = self.exclude + else: + allow_patterns = self.filenames + ignore_patterns = None + + return snapshot_download( + repo_id=self.repo_id, + repo_type=self.repo_type, + revision=self.revision, + allow_patterns=allow_patterns, + ignore_patterns=ignore_patterns, + resume_download=self.resume_download, + force_download=self.force_download, + cache_dir=self.cache_dir, + token=self.token, + local_dir=self.local_dir, + library_name="huggingface-cli", + max_workers=self.max_workers, + ) diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/commands/env.py b/venv/lib/python3.13/site-packages/huggingface_hub/commands/env.py new file mode 100644 index 0000000000000000000000000000000000000000..ad674738b2f137ec0b79c11ef35057a351de6d86 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/commands/env.py @@ -0,0 +1,39 @@ +# Copyright 2022 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains command to print information about the environment. + +Usage: + huggingface-cli env +""" + +from argparse import _SubParsersAction + +from ..utils import dump_environment_info +from . import BaseHuggingfaceCLICommand +from ._cli_utils import show_deprecation_warning + + +class EnvironmentCommand(BaseHuggingfaceCLICommand): + def __init__(self, args): + self.args = args + + @staticmethod + def register_subcommand(parser: _SubParsersAction): + env_parser = parser.add_parser("env", help="Print information about the environment.") + env_parser.set_defaults(func=EnvironmentCommand) + + def run(self) -> None: + show_deprecation_warning("huggingface-cli env", "hf env") + + dump_environment_info() diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/commands/huggingface_cli.py b/venv/lib/python3.13/site-packages/huggingface_hub/commands/huggingface_cli.py new file mode 100644 index 0000000000000000000000000000000000000000..697c85d1e386d9c954be0f8112cb12e1bc84e7fe --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/commands/huggingface_cli.py @@ -0,0 +1,65 @@ +# Copyright 2020 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from argparse import ArgumentParser + +from huggingface_hub.commands._cli_utils import show_deprecation_warning +from huggingface_hub.commands.delete_cache import DeleteCacheCommand +from huggingface_hub.commands.download import DownloadCommand +from huggingface_hub.commands.env import EnvironmentCommand +from huggingface_hub.commands.lfs import LfsCommands +from huggingface_hub.commands.repo import RepoCommands +from huggingface_hub.commands.repo_files import RepoFilesCommand +from huggingface_hub.commands.scan_cache import ScanCacheCommand +from huggingface_hub.commands.tag import TagCommands +from huggingface_hub.commands.upload import UploadCommand +from huggingface_hub.commands.upload_large_folder import UploadLargeFolderCommand +from huggingface_hub.commands.user import UserCommands +from huggingface_hub.commands.version import VersionCommand + + +def main(): + parser = ArgumentParser("huggingface-cli", usage="huggingface-cli []") + commands_parser = parser.add_subparsers(help="huggingface-cli command helpers") + + # Register commands + DownloadCommand.register_subcommand(commands_parser) + UploadCommand.register_subcommand(commands_parser) + RepoFilesCommand.register_subcommand(commands_parser) + EnvironmentCommand.register_subcommand(commands_parser) + UserCommands.register_subcommand(commands_parser) + RepoCommands.register_subcommand(commands_parser) + LfsCommands.register_subcommand(commands_parser) + ScanCacheCommand.register_subcommand(commands_parser) + DeleteCacheCommand.register_subcommand(commands_parser) + TagCommands.register_subcommand(commands_parser) + VersionCommand.register_subcommand(commands_parser) + + # Experimental + UploadLargeFolderCommand.register_subcommand(commands_parser) + + # Let's go + args = parser.parse_args() + if not hasattr(args, "func"): + show_deprecation_warning("huggingface-cli", "hf") + parser.print_help() + exit(1) + + # Run + service = args.func(args) + service.run() + + +if __name__ == "__main__": + main() diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/commands/lfs.py b/venv/lib/python3.13/site-packages/huggingface_hub/commands/lfs.py new file mode 100644 index 0000000000000000000000000000000000000000..e510e345e6a4bf6da03f71b35cbfa2a4f0eb7325 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/commands/lfs.py @@ -0,0 +1,200 @@ +""" +Implementation of a custom transfer agent for the transfer type "multipart" for +git-lfs. + +Inspired by: +github.com/cbartz/git-lfs-swift-transfer-agent/blob/master/git_lfs_swift_transfer.py + +Spec is: github.com/git-lfs/git-lfs/blob/master/docs/custom-transfers.md + + +To launch debugger while developing: + +``` [lfs "customtransfer.multipart"] +path = /path/to/huggingface_hub/.env/bin/python args = -m debugpy --listen 5678 +--wait-for-client +/path/to/huggingface_hub/src/huggingface_hub/commands/huggingface_cli.py +lfs-multipart-upload ```""" + +import json +import os +import subprocess +import sys +from argparse import _SubParsersAction +from typing import Dict, List, Optional + +from huggingface_hub.commands import BaseHuggingfaceCLICommand +from huggingface_hub.lfs import LFS_MULTIPART_UPLOAD_COMMAND + +from ..utils import get_session, hf_raise_for_status, logging +from ..utils._lfs import SliceFileObj + + +logger = logging.get_logger(__name__) + + +class LfsCommands(BaseHuggingfaceCLICommand): + """ + Implementation of a custom transfer agent for the transfer type "multipart" + for git-lfs. This lets users upload large files >5GB 🔥. Spec for LFS custom + transfer agent is: + https://github.com/git-lfs/git-lfs/blob/master/docs/custom-transfers.md + + This introduces two commands to the CLI: + + 1. $ huggingface-cli lfs-enable-largefiles + + This should be executed once for each model repo that contains a model file + >5GB. It's documented in the error message you get if you just try to git + push a 5GB file without having enabled it before. + + 2. $ huggingface-cli lfs-multipart-upload + + This command is called by lfs directly and is not meant to be called by the + user. + """ + + @staticmethod + def register_subcommand(parser: _SubParsersAction): + enable_parser = parser.add_parser( + "lfs-enable-largefiles", help="Configure your repository to enable upload of files > 5GB." + ) + enable_parser.add_argument("path", type=str, help="Local path to repository you want to configure.") + enable_parser.set_defaults(func=lambda args: LfsEnableCommand(args)) + + # Command will get called by git-lfs, do not call it directly. + upload_parser = parser.add_parser(LFS_MULTIPART_UPLOAD_COMMAND, add_help=False) + upload_parser.set_defaults(func=lambda args: LfsUploadCommand(args)) + + +class LfsEnableCommand: + def __init__(self, args): + self.args = args + + def run(self): + local_path = os.path.abspath(self.args.path) + if not os.path.isdir(local_path): + print("This does not look like a valid git repo.") + exit(1) + subprocess.run( + "git config lfs.customtransfer.multipart.path huggingface-cli".split(), + check=True, + cwd=local_path, + ) + subprocess.run( + f"git config lfs.customtransfer.multipart.args {LFS_MULTIPART_UPLOAD_COMMAND}".split(), + check=True, + cwd=local_path, + ) + print("Local repo set up for largefiles") + + +def write_msg(msg: Dict): + """Write out the message in Line delimited JSON.""" + msg_str = json.dumps(msg) + "\n" + sys.stdout.write(msg_str) + sys.stdout.flush() + + +def read_msg() -> Optional[Dict]: + """Read Line delimited JSON from stdin.""" + msg = json.loads(sys.stdin.readline().strip()) + + if "terminate" in (msg.get("type"), msg.get("event")): + # terminate message received + return None + + if msg.get("event") not in ("download", "upload"): + logger.critical("Received unexpected message") + sys.exit(1) + + return msg + + +class LfsUploadCommand: + def __init__(self, args) -> None: + self.args = args + + def run(self) -> None: + # Immediately after invoking a custom transfer process, git-lfs + # sends initiation data to the process over stdin. + # This tells the process useful information about the configuration. + init_msg = json.loads(sys.stdin.readline().strip()) + if not (init_msg.get("event") == "init" and init_msg.get("operation") == "upload"): + write_msg({"error": {"code": 32, "message": "Wrong lfs init operation"}}) + sys.exit(1) + + # The transfer process should use the information it needs from the + # initiation structure, and also perform any one-off setup tasks it + # needs to do. It should then respond on stdout with a simple empty + # confirmation structure, as follows: + write_msg({}) + + # After the initiation exchange, git-lfs will send any number of + # transfer requests to the stdin of the transfer process, in a serial sequence. + while True: + msg = read_msg() + if msg is None: + # When all transfers have been processed, git-lfs will send + # a terminate event to the stdin of the transfer process. + # On receiving this message the transfer process should + # clean up and terminate. No response is expected. + sys.exit(0) + + oid = msg["oid"] + filepath = msg["path"] + completion_url = msg["action"]["href"] + header = msg["action"]["header"] + chunk_size = int(header.pop("chunk_size")) + presigned_urls: List[str] = list(header.values()) + + # Send a "started" progress event to allow other workers to start. + # Otherwise they're delayed until first "progress" event is reported, + # i.e. after the first 5GB by default (!) + write_msg( + { + "event": "progress", + "oid": oid, + "bytesSoFar": 1, + "bytesSinceLast": 0, + } + ) + + parts = [] + with open(filepath, "rb") as file: + for i, presigned_url in enumerate(presigned_urls): + with SliceFileObj( + file, + seek_from=i * chunk_size, + read_limit=chunk_size, + ) as data: + r = get_session().put(presigned_url, data=data) + hf_raise_for_status(r) + parts.append( + { + "etag": r.headers.get("etag"), + "partNumber": i + 1, + } + ) + # In order to support progress reporting while data is uploading / downloading, + # the transfer process should post messages to stdout + write_msg( + { + "event": "progress", + "oid": oid, + "bytesSoFar": (i + 1) * chunk_size, + "bytesSinceLast": chunk_size, + } + ) + # Not precise but that's ok. + + r = get_session().post( + completion_url, + json={ + "oid": oid, + "parts": parts, + }, + ) + hf_raise_for_status(r) + + write_msg({"event": "complete", "oid": oid}) diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/commands/repo.py b/venv/lib/python3.13/site-packages/huggingface_hub/commands/repo.py new file mode 100644 index 0000000000000000000000000000000000000000..fe75349d67bdc0314afe737daa7224b2a090f810 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/commands/repo.py @@ -0,0 +1,151 @@ +# Copyright 2025 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains commands to interact with repositories on the Hugging Face Hub. + +Usage: + # create a new dataset repo on the Hub + huggingface-cli repo create my-cool-dataset --repo-type=dataset + + # create a private model repo on the Hub + huggingface-cli repo create my-cool-model --private +""" + +import argparse +from argparse import _SubParsersAction +from typing import Optional + +from huggingface_hub.commands import BaseHuggingfaceCLICommand +from huggingface_hub.commands._cli_utils import ANSI +from huggingface_hub.constants import SPACES_SDK_TYPES +from huggingface_hub.hf_api import HfApi +from huggingface_hub.utils import logging + +from ._cli_utils import show_deprecation_warning + + +logger = logging.get_logger(__name__) + + +class RepoCommands(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction): + repo_parser = parser.add_parser("repo", help="{create} Commands to interact with your huggingface.co repos.") + repo_subparsers = repo_parser.add_subparsers(help="huggingface.co repos related commands") + repo_create_parser = repo_subparsers.add_parser("create", help="Create a new repo on huggingface.co") + repo_create_parser.add_argument( + "repo_id", + type=str, + help="The ID of the repo to create to (e.g. `username/repo-name`). The username is optional and will be set to your username if not provided.", + ) + repo_create_parser.add_argument( + "--repo-type", + type=str, + help='Optional: set to "dataset" or "space" if creating a dataset or space, default is model.', + ) + repo_create_parser.add_argument( + "--space_sdk", + type=str, + help='Optional: Hugging Face Spaces SDK type. Required when --type is set to "space".', + choices=SPACES_SDK_TYPES, + ) + repo_create_parser.add_argument( + "--private", + action="store_true", + help="Whether to create a private repository. Defaults to public unless the organization's default is private.", + ) + repo_create_parser.add_argument( + "--token", + type=str, + help="Hugging Face token. Will default to the locally saved token if not provided.", + ) + repo_create_parser.add_argument( + "--exist-ok", + action="store_true", + help="Do not raise an error if repo already exists.", + ) + repo_create_parser.add_argument( + "--resource-group-id", + type=str, + help="Resource group in which to create the repo. Resource groups is only available for Enterprise Hub organizations.", + ) + repo_create_parser.add_argument( + "--type", + type=str, + help="[Deprecated]: use --repo-type instead.", + ) + repo_create_parser.add_argument( + "-y", + "--yes", + action="store_true", + help="[Deprecated] no effect.", + ) + repo_create_parser.add_argument( + "--organization", type=str, help="[Deprecated] Pass the organization namespace directly in the repo_id." + ) + repo_create_parser.set_defaults(func=lambda args: RepoCreateCommand(args)) + + +class RepoCreateCommand: + def __init__(self, args: argparse.Namespace): + self.repo_id: str = args.repo_id + self.repo_type: Optional[str] = args.repo_type or args.type + self.space_sdk: Optional[str] = args.space_sdk + self.organization: Optional[str] = args.organization + self.yes: bool = args.yes + self.private: bool = args.private + self.token: Optional[str] = args.token + self.exist_ok: bool = args.exist_ok + self.resource_group_id: Optional[str] = args.resource_group_id + + if args.type is not None: + print( + ANSI.yellow( + "The --type argument is deprecated and will be removed in a future version. Use --repo-type instead." + ) + ) + if self.organization is not None: + print( + ANSI.yellow( + "The --organization argument is deprecated and will be removed in a future version. Pass the organization namespace directly in the repo_id." + ) + ) + if self.yes: + print( + ANSI.yellow( + "The --yes argument is deprecated and will be removed in a future version. It does not have any effect." + ) + ) + + self._api = HfApi() + + def run(self): + show_deprecation_warning("huggingface-cli repo", "hf repo") + + if self.organization is not None: + if "/" in self.repo_id: + print(ANSI.red("You cannot pass both --organization and a repo_id with a namespace.")) + exit(1) + self.repo_id = f"{self.organization}/{self.repo_id}" + + repo_url = self._api.create_repo( + repo_id=self.repo_id, + repo_type=self.repo_type, + private=self.private, + token=self.token, + exist_ok=self.exist_ok, + resource_group_id=self.resource_group_id, + space_sdk=self.space_sdk, + ) + print(f"Successfully created {ANSI.bold(repo_url.repo_id)} on the Hub.") + print(f"Your repo is now available at {ANSI.bold(repo_url)}") diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/commands/repo_files.py b/venv/lib/python3.13/site-packages/huggingface_hub/commands/repo_files.py new file mode 100644 index 0000000000000000000000000000000000000000..da9685315ea67dc9d1e9921ecb2656244cae8783 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/commands/repo_files.py @@ -0,0 +1,132 @@ +# coding=utf-8 +# Copyright 2023-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains command to update or delete files in a repository using the CLI. + +Usage: + # delete all + huggingface-cli repo-files delete "*" + + # delete single file + huggingface-cli repo-files delete file.txt + + # delete single folder + huggingface-cli repo-files delete folder/ + + # delete multiple + huggingface-cli repo-files delete file.txt folder/ file2.txt + + # delete multiple patterns + huggingface-cli repo-files delete file.txt "*.json" "folder/*.parquet" + + # delete from different revision / repo-type + huggingface-cli repo-files delete file.txt --revision=refs/pr/1 --repo-type=dataset +""" + +from argparse import _SubParsersAction +from typing import List, Optional + +from huggingface_hub import logging +from huggingface_hub.commands import BaseHuggingfaceCLICommand +from huggingface_hub.hf_api import HfApi + +from ._cli_utils import show_deprecation_warning + + +logger = logging.get_logger(__name__) + + +class DeleteFilesSubCommand: + def __init__(self, args) -> None: + self.args = args + self.repo_id: str = args.repo_id + self.repo_type: Optional[str] = args.repo_type + self.revision: Optional[str] = args.revision + self.api: HfApi = HfApi(token=args.token, library_name="huggingface-cli") + self.patterns: List[str] = args.patterns + self.commit_message: Optional[str] = args.commit_message + self.commit_description: Optional[str] = args.commit_description + self.create_pr: bool = args.create_pr + self.token: Optional[str] = args.token + + def run(self) -> None: + show_deprecation_warning("huggingface-cli repo-files", "hf repo-files") + + logging.set_verbosity_info() + url = self.api.delete_files( + delete_patterns=self.patterns, + repo_id=self.repo_id, + repo_type=self.repo_type, + revision=self.revision, + commit_message=self.commit_message, + commit_description=self.commit_description, + create_pr=self.create_pr, + ) + print(f"Files correctly deleted from repo. Commit: {url}.") + logging.set_verbosity_warning() + + +class RepoFilesCommand(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction): + repo_files_parser = parser.add_parser("repo-files", help="Manage files in a repo on the Hub") + repo_files_parser.add_argument( + "repo_id", type=str, help="The ID of the repo to manage (e.g. `username/repo-name`)." + ) + repo_files_subparsers = repo_files_parser.add_subparsers( + help="Action to execute against the files.", + required=True, + ) + delete_subparser = repo_files_subparsers.add_parser( + "delete", + help="Delete files from a repo on the Hub", + ) + delete_subparser.set_defaults(func=lambda args: DeleteFilesSubCommand(args)) + delete_subparser.add_argument( + "patterns", + nargs="+", + type=str, + help="Glob patterns to match files to delete.", + ) + delete_subparser.add_argument( + "--repo-type", + choices=["model", "dataset", "space"], + default="model", + help="Type of the repo to upload to (e.g. `dataset`).", + ) + delete_subparser.add_argument( + "--revision", + type=str, + help=( + "An optional Git revision to push to. It can be a branch name " + "or a PR reference. If revision does not" + " exist and `--create-pr` is not set, a branch will be automatically created." + ), + ) + delete_subparser.add_argument( + "--commit-message", type=str, help="The summary / title / first line of the generated commit." + ) + delete_subparser.add_argument( + "--commit-description", type=str, help="The description of the generated commit." + ) + delete_subparser.add_argument( + "--create-pr", action="store_true", help="Whether to create a new Pull Request for these changes." + ) + repo_files_parser.add_argument( + "--token", + type=str, + help="A User Access Token generated from https://huggingface.co/settings/tokens", + ) + + repo_files_parser.set_defaults(func=RepoFilesCommand) diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/commands/scan_cache.py b/venv/lib/python3.13/site-packages/huggingface_hub/commands/scan_cache.py new file mode 100644 index 0000000000000000000000000000000000000000..711a5d09cc2b64b9c7f22a298e26a198b4dc48f1 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/commands/scan_cache.py @@ -0,0 +1,183 @@ +# coding=utf-8 +# Copyright 2022-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains command to scan the HF cache directory. + +Usage: + huggingface-cli scan-cache + huggingface-cli scan-cache -v + huggingface-cli scan-cache -vvv + huggingface-cli scan-cache --dir ~/.cache/huggingface/hub +""" + +import time +from argparse import Namespace, _SubParsersAction +from typing import Optional + +from ..utils import CacheNotFound, HFCacheInfo, scan_cache_dir +from . import BaseHuggingfaceCLICommand +from ._cli_utils import ANSI, show_deprecation_warning, tabulate + + +class ScanCacheCommand(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction): + scan_cache_parser = parser.add_parser("scan-cache", help="Scan cache directory.") + + scan_cache_parser.add_argument( + "--dir", + type=str, + default=None, + help="cache directory to scan (optional). Default to the default HuggingFace cache.", + ) + scan_cache_parser.add_argument( + "-v", + "--verbose", + action="count", + default=0, + help="show a more verbose output", + ) + scan_cache_parser.set_defaults(func=ScanCacheCommand) + + def __init__(self, args: Namespace) -> None: + self.verbosity: int = args.verbose + self.cache_dir: Optional[str] = args.dir + + def run(self): + show_deprecation_warning("huggingface-cli scan-cache", "hf cache scan") + + try: + t0 = time.time() + hf_cache_info = scan_cache_dir(self.cache_dir) + t1 = time.time() + except CacheNotFound as exc: + cache_dir = exc.cache_dir + print(f"Cache directory not found: {cache_dir}") + return + + self._print_hf_cache_info_as_table(hf_cache_info) + + print( + f"\nDone in {round(t1 - t0, 1)}s. Scanned {len(hf_cache_info.repos)} repo(s)" + f" for a total of {ANSI.red(hf_cache_info.size_on_disk_str)}." + ) + if len(hf_cache_info.warnings) > 0: + message = f"Got {len(hf_cache_info.warnings)} warning(s) while scanning." + if self.verbosity >= 3: + print(ANSI.gray(message)) + for warning in hf_cache_info.warnings: + print(ANSI.gray(str(warning))) + else: + print(ANSI.gray(message + " Use -vvv to print details.")) + + def _print_hf_cache_info_as_table(self, hf_cache_info: HFCacheInfo) -> None: + print(get_table(hf_cache_info, verbosity=self.verbosity)) + + +def get_table(hf_cache_info: HFCacheInfo, *, verbosity: int = 0) -> str: + """Generate a table from the [`HFCacheInfo`] object. + + Pass `verbosity=0` to get a table with a single row per repo, with columns + "repo_id", "repo_type", "size_on_disk", "nb_files", "last_accessed", "last_modified", "refs", "local_path". + + Pass `verbosity=1` to get a table with a row per repo and revision (thus multiple rows can appear for a single repo), with columns + "repo_id", "repo_type", "revision", "size_on_disk", "nb_files", "last_modified", "refs", "local_path". + + Example: + ```py + >>> from huggingface_hub.utils import scan_cache_dir + >>> from huggingface_hub.commands.scan_cache import get_table + + >>> hf_cache_info = scan_cache_dir() + HFCacheInfo(...) + + >>> print(get_table(hf_cache_info, verbosity=0)) + REPO ID REPO TYPE SIZE ON DISK NB FILES LAST_ACCESSED LAST_MODIFIED REFS LOCAL PATH + --------------------------------------------------- --------- ------------ -------- ------------- ------------- ---- -------------------------------------------------------------------------------------------------- + roberta-base model 2.7M 5 1 day ago 1 week ago main C:\\Users\\admin\\.cache\\huggingface\\hub\\models--roberta-base + suno/bark model 8.8K 1 1 week ago 1 week ago main C:\\Users\\admin\\.cache\\huggingface\\hub\\models--suno--bark + t5-base model 893.8M 4 4 days ago 7 months ago main C:\\Users\\admin\\.cache\\huggingface\\hub\\models--t5-base + t5-large model 3.0G 4 5 weeks ago 5 months ago main C:\\Users\\admin\\.cache\\huggingface\\hub\\models--t5-large + + >>> print(get_table(hf_cache_info, verbosity=1)) + REPO ID REPO TYPE REVISION SIZE ON DISK NB FILES LAST_MODIFIED REFS LOCAL PATH + --------------------------------------------------- --------- ---------------------------------------- ------------ -------- ------------- ---- ----------------------------------------------------------------------------------------------------------------------------------------------------- + roberta-base model e2da8e2f811d1448a5b465c236feacd80ffbac7b 2.7M 5 1 week ago main C:\\Users\\admin\\.cache\\huggingface\\hub\\models--roberta-base\\snapshots\\e2da8e2f811d1448a5b465c236feacd80ffbac7b + suno/bark model 70a8a7d34168586dc5d028fa9666aceade177992 8.8K 1 1 week ago main C:\\Users\\admin\\.cache\\huggingface\\hub\\models--suno--bark\\snapshots\\70a8a7d34168586dc5d028fa9666aceade177992 + t5-base model a9723ea7f1b39c1eae772870f3b547bf6ef7e6c1 893.8M 4 7 months ago main C:\\Users\\admin\\.cache\\huggingface\\hub\\models--t5-base\\snapshots\\a9723ea7f1b39c1eae772870f3b547bf6ef7e6c1 + t5-large model 150ebc2c4b72291e770f58e6057481c8d2ed331a 3.0G 4 5 months ago main C:\\Users\\admin\\.cache\\huggingface\\hub\\models--t5-large\\snapshots\\150ebc2c4b72291e770f58e6057481c8d2ed331a ``` + ``` + + Args: + hf_cache_info ([`HFCacheInfo`]): + The HFCacheInfo object to print. + verbosity (`int`, *optional*): + The verbosity level. Defaults to 0. + + Returns: + `str`: The table as a string. + """ + if verbosity == 0: + return tabulate( + rows=[ + [ + repo.repo_id, + repo.repo_type, + "{:>12}".format(repo.size_on_disk_str), + repo.nb_files, + repo.last_accessed_str, + repo.last_modified_str, + ", ".join(sorted(repo.refs)), + str(repo.repo_path), + ] + for repo in sorted(hf_cache_info.repos, key=lambda repo: repo.repo_path) + ], + headers=[ + "REPO ID", + "REPO TYPE", + "SIZE ON DISK", + "NB FILES", + "LAST_ACCESSED", + "LAST_MODIFIED", + "REFS", + "LOCAL PATH", + ], + ) + else: + return tabulate( + rows=[ + [ + repo.repo_id, + repo.repo_type, + revision.commit_hash, + "{:>12}".format(revision.size_on_disk_str), + revision.nb_files, + revision.last_modified_str, + ", ".join(sorted(revision.refs)), + str(revision.snapshot_path), + ] + for repo in sorted(hf_cache_info.repos, key=lambda repo: repo.repo_path) + for revision in sorted(repo.revisions, key=lambda revision: revision.commit_hash) + ], + headers=[ + "REPO ID", + "REPO TYPE", + "REVISION", + "SIZE ON DISK", + "NB FILES", + "LAST_MODIFIED", + "REFS", + "LOCAL PATH", + ], + ) diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/commands/tag.py b/venv/lib/python3.13/site-packages/huggingface_hub/commands/tag.py new file mode 100644 index 0000000000000000000000000000000000000000..405d407f8135d940cf078f905a6e66acd4b1dacc --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/commands/tag.py @@ -0,0 +1,161 @@ +# coding=utf-8 +# Copyright 2024-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Contains commands to perform tag management with the CLI. + +Usage Examples: + - Create a tag: + $ huggingface-cli tag user/my-model 1.0 --message "First release" + $ huggingface-cli tag user/my-model 1.0 -m "First release" --revision develop + $ huggingface-cli tag user/my-dataset 1.0 -m "First release" --repo-type dataset + $ huggingface-cli tag user/my-space 1.0 + - List all tags: + $ huggingface-cli tag -l user/my-model + $ huggingface-cli tag --list user/my-dataset --repo-type dataset + - Delete a tag: + $ huggingface-cli tag -d user/my-model 1.0 + $ huggingface-cli tag --delete user/my-dataset 1.0 --repo-type dataset + $ huggingface-cli tag -d user/my-space 1.0 -y +""" + +from argparse import Namespace, _SubParsersAction + +from requests.exceptions import HTTPError + +from huggingface_hub.commands import BaseHuggingfaceCLICommand +from huggingface_hub.constants import ( + REPO_TYPES, +) +from huggingface_hub.hf_api import HfApi + +from ..errors import HfHubHTTPError, RepositoryNotFoundError, RevisionNotFoundError +from ._cli_utils import ANSI, show_deprecation_warning + + +class TagCommands(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction): + tag_parser = parser.add_parser("tag", help="(create, list, delete) tags for a repo in the hub") + + tag_parser.add_argument("repo_id", type=str, help="The ID of the repo to tag (e.g. `username/repo-name`).") + tag_parser.add_argument("tag", nargs="?", type=str, help="The name of the tag for creation or deletion.") + tag_parser.add_argument("-m", "--message", type=str, help="The description of the tag to create.") + tag_parser.add_argument("--revision", type=str, help="The git revision to tag.") + tag_parser.add_argument( + "--token", type=str, help="A User Access Token generated from https://huggingface.co/settings/tokens." + ) + tag_parser.add_argument( + "--repo-type", + choices=["model", "dataset", "space"], + default="model", + help="Set the type of repository (model, dataset, or space).", + ) + tag_parser.add_argument("-y", "--yes", action="store_true", help="Answer Yes to prompts automatically.") + + tag_parser.add_argument("-l", "--list", action="store_true", help="List tags for a repository.") + tag_parser.add_argument("-d", "--delete", action="store_true", help="Delete a tag for a repository.") + + tag_parser.set_defaults(func=lambda args: handle_commands(args)) + + +def handle_commands(args: Namespace): + show_deprecation_warning("huggingface-cli tag", "hf repo tag") + + if args.list: + return TagListCommand(args) + elif args.delete: + return TagDeleteCommand(args) + else: + return TagCreateCommand(args) + + +class TagCommand: + def __init__(self, args: Namespace): + self.args = args + self.api = HfApi(token=self.args.token) + self.repo_id = self.args.repo_id + self.repo_type = self.args.repo_type + if self.repo_type not in REPO_TYPES: + print("Invalid repo --repo-type") + exit(1) + + +class TagCreateCommand(TagCommand): + def run(self): + print(f"You are about to create tag {ANSI.bold(self.args.tag)} on {self.repo_type} {ANSI.bold(self.repo_id)}") + + try: + self.api.create_tag( + repo_id=self.repo_id, + tag=self.args.tag, + tag_message=self.args.message, + revision=self.args.revision, + repo_type=self.repo_type, + ) + except RepositoryNotFoundError: + print(f"{self.repo_type.capitalize()} {ANSI.bold(self.repo_id)} not found.") + exit(1) + except RevisionNotFoundError: + print(f"Revision {ANSI.bold(self.args.revision)} not found.") + exit(1) + except HfHubHTTPError as e: + if e.response.status_code == 409: + print(f"Tag {ANSI.bold(self.args.tag)} already exists on {ANSI.bold(self.repo_id)}") + exit(1) + raise e + + print(f"Tag {ANSI.bold(self.args.tag)} created on {ANSI.bold(self.repo_id)}") + + +class TagListCommand(TagCommand): + def run(self): + try: + refs = self.api.list_repo_refs( + repo_id=self.repo_id, + repo_type=self.repo_type, + ) + except RepositoryNotFoundError: + print(f"{self.repo_type.capitalize()} {ANSI.bold(self.repo_id)} not found.") + exit(1) + except HTTPError as e: + print(e) + print(ANSI.red(e.response.text)) + exit(1) + if len(refs.tags) == 0: + print("No tags found") + exit(0) + print(f"Tags for {self.repo_type} {ANSI.bold(self.repo_id)}:") + for tag in refs.tags: + print(tag.name) + + +class TagDeleteCommand(TagCommand): + def run(self): + print(f"You are about to delete tag {ANSI.bold(self.args.tag)} on {self.repo_type} {ANSI.bold(self.repo_id)}") + + if not self.args.yes: + choice = input("Proceed? [Y/n] ").lower() + if choice not in ("", "y", "yes"): + print("Abort") + exit() + try: + self.api.delete_tag(repo_id=self.repo_id, tag=self.args.tag, repo_type=self.repo_type) + except RepositoryNotFoundError: + print(f"{self.repo_type.capitalize()} {ANSI.bold(self.repo_id)} not found.") + exit(1) + except RevisionNotFoundError: + print(f"Tag {ANSI.bold(self.args.tag)} not found on {ANSI.bold(self.repo_id)}") + exit(1) + print(f"Tag {ANSI.bold(self.args.tag)} deleted on {ANSI.bold(self.repo_id)}") diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/commands/upload.py b/venv/lib/python3.13/site-packages/huggingface_hub/commands/upload.py new file mode 100644 index 0000000000000000000000000000000000000000..c778555cda56eb17c905f0728fef6712acc75cb8 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/commands/upload.py @@ -0,0 +1,318 @@ +# coding=utf-8 +# Copyright 2023-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains command to upload a repo or file with the CLI. + +Usage: + # Upload file (implicit) + huggingface-cli upload my-cool-model ./my-cool-model.safetensors + + # Upload file (explicit) + huggingface-cli upload my-cool-model ./my-cool-model.safetensors model.safetensors + + # Upload directory (implicit). If `my-cool-model/` is a directory it will be uploaded, otherwise an exception is raised. + huggingface-cli upload my-cool-model + + # Upload directory (explicit) + huggingface-cli upload my-cool-model ./models/my-cool-model . + + # Upload filtered directory (example: tensorboard logs except for the last run) + huggingface-cli upload my-cool-model ./model/training /logs --include "*.tfevents.*" --exclude "*20230905*" + + # Upload with wildcard + huggingface-cli upload my-cool-model "./model/training/*.safetensors" + + # Upload private dataset + huggingface-cli upload Wauplin/my-cool-dataset ./data . --repo-type=dataset --private + + # Upload with token + huggingface-cli upload Wauplin/my-cool-model --token=hf_**** + + # Sync local Space with Hub (upload new files, delete removed files) + huggingface-cli upload Wauplin/space-example --repo-type=space --exclude="/logs/*" --delete="*" --commit-message="Sync local Space with Hub" + + # Schedule commits every 30 minutes + huggingface-cli upload Wauplin/my-cool-model --every=30 +""" + +import os +import time +import warnings +from argparse import Namespace, _SubParsersAction +from typing import List, Optional + +from huggingface_hub import logging +from huggingface_hub._commit_scheduler import CommitScheduler +from huggingface_hub.commands import BaseHuggingfaceCLICommand +from huggingface_hub.constants import HF_HUB_ENABLE_HF_TRANSFER +from huggingface_hub.errors import RevisionNotFoundError +from huggingface_hub.hf_api import HfApi +from huggingface_hub.utils import disable_progress_bars, enable_progress_bars +from huggingface_hub.utils._runtime import is_xet_available + +from ._cli_utils import show_deprecation_warning + + +logger = logging.get_logger(__name__) + + +class UploadCommand(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction): + upload_parser = parser.add_parser("upload", help="Upload a file or a folder to a repo on the Hub") + upload_parser.add_argument( + "repo_id", type=str, help="The ID of the repo to upload to (e.g. `username/repo-name`)." + ) + upload_parser.add_argument( + "local_path", + nargs="?", + help="Local path to the file or folder to upload. Wildcard patterns are supported. Defaults to current directory.", + ) + upload_parser.add_argument( + "path_in_repo", + nargs="?", + help="Path of the file or folder in the repo. Defaults to the relative path of the file or folder.", + ) + upload_parser.add_argument( + "--repo-type", + choices=["model", "dataset", "space"], + default="model", + help="Type of the repo to upload to (e.g. `dataset`).", + ) + upload_parser.add_argument( + "--revision", + type=str, + help=( + "An optional Git revision to push to. It can be a branch name or a PR reference. If revision does not" + " exist and `--create-pr` is not set, a branch will be automatically created." + ), + ) + upload_parser.add_argument( + "--private", + action="store_true", + help=( + "Whether to create a private repo if repo doesn't exist on the Hub. Ignored if the repo already" + " exists." + ), + ) + upload_parser.add_argument("--include", nargs="*", type=str, help="Glob patterns to match files to upload.") + upload_parser.add_argument( + "--exclude", nargs="*", type=str, help="Glob patterns to exclude from files to upload." + ) + upload_parser.add_argument( + "--delete", + nargs="*", + type=str, + help="Glob patterns for file to be deleted from the repo while committing.", + ) + upload_parser.add_argument( + "--commit-message", type=str, help="The summary / title / first line of the generated commit." + ) + upload_parser.add_argument("--commit-description", type=str, help="The description of the generated commit.") + upload_parser.add_argument( + "--create-pr", action="store_true", help="Whether to upload content as a new Pull Request." + ) + upload_parser.add_argument( + "--every", + type=float, + help="If set, a background job is scheduled to create commits every `every` minutes.", + ) + upload_parser.add_argument( + "--token", type=str, help="A User Access Token generated from https://huggingface.co/settings/tokens" + ) + upload_parser.add_argument( + "--quiet", + action="store_true", + help="If True, progress bars are disabled and only the path to the uploaded files is printed.", + ) + upload_parser.set_defaults(func=UploadCommand) + + def __init__(self, args: Namespace) -> None: + self.repo_id: str = args.repo_id + self.repo_type: Optional[str] = args.repo_type + self.revision: Optional[str] = args.revision + self.private: bool = args.private + + self.include: Optional[List[str]] = args.include + self.exclude: Optional[List[str]] = args.exclude + self.delete: Optional[List[str]] = args.delete + + self.commit_message: Optional[str] = args.commit_message + self.commit_description: Optional[str] = args.commit_description + self.create_pr: bool = args.create_pr + self.api: HfApi = HfApi(token=args.token, library_name="huggingface-cli") + self.quiet: bool = args.quiet # disable warnings and progress bars + + # Check `--every` is valid + if args.every is not None and args.every <= 0: + raise ValueError(f"`every` must be a positive value (got '{args.every}')") + self.every: Optional[float] = args.every + + # Resolve `local_path` and `path_in_repo` + repo_name: str = args.repo_id.split("/")[-1] # e.g. "Wauplin/my-cool-model" => "my-cool-model" + self.local_path: str + self.path_in_repo: str + + if args.local_path is not None and any(c in args.local_path for c in ["*", "?", "["]): + if args.include is not None: + raise ValueError("Cannot set `--include` when passing a `local_path` containing a wildcard.") + if args.path_in_repo is not None and args.path_in_repo != ".": + raise ValueError("Cannot set `path_in_repo` when passing a `local_path` containing a wildcard.") + self.local_path = "." + self.include = args.local_path + self.path_in_repo = "." + elif args.local_path is None and os.path.isfile(repo_name): + # Implicit case 1: user provided only a repo_id which happen to be a local file as well => upload it with same name + self.local_path = repo_name + self.path_in_repo = repo_name + elif args.local_path is None and os.path.isdir(repo_name): + # Implicit case 2: user provided only a repo_id which happen to be a local folder as well => upload it at root + self.local_path = repo_name + self.path_in_repo = "." + elif args.local_path is None: + # Implicit case 3: user provided only a repo_id that does not match a local file or folder + # => the user must explicitly provide a local_path => raise exception + raise ValueError(f"'{repo_name}' is not a local file or folder. Please set `local_path` explicitly.") + elif args.path_in_repo is None and os.path.isfile(args.local_path): + # Explicit local path to file, no path in repo => upload it at root with same name + self.local_path = args.local_path + self.path_in_repo = os.path.basename(args.local_path) + elif args.path_in_repo is None: + # Explicit local path to folder, no path in repo => upload at root + self.local_path = args.local_path + self.path_in_repo = "." + else: + # Finally, if both paths are explicit + self.local_path = args.local_path + self.path_in_repo = args.path_in_repo + + def run(self) -> None: + show_deprecation_warning("huggingface-cli upload", "hf upload") + + if self.quiet: + disable_progress_bars() + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + print(self._upload()) + enable_progress_bars() + else: + logging.set_verbosity_info() + print(self._upload()) + logging.set_verbosity_warning() + + def _upload(self) -> str: + if os.path.isfile(self.local_path): + if self.include is not None and len(self.include) > 0: + warnings.warn("Ignoring `--include` since a single file is uploaded.") + if self.exclude is not None and len(self.exclude) > 0: + warnings.warn("Ignoring `--exclude` since a single file is uploaded.") + if self.delete is not None and len(self.delete) > 0: + warnings.warn("Ignoring `--delete` since a single file is uploaded.") + + if not is_xet_available() and not HF_HUB_ENABLE_HF_TRANSFER: + logger.info( + "Consider using `hf_transfer` for faster uploads. This solution comes with some limitations. See" + " https://huggingface.co/docs/huggingface_hub/hf_transfer for more details." + ) + + # Schedule commits if `every` is set + if self.every is not None: + if os.path.isfile(self.local_path): + # If file => watch entire folder + use allow_patterns + folder_path = os.path.dirname(self.local_path) + path_in_repo = ( + self.path_in_repo[: -len(self.local_path)] # remove filename from path_in_repo + if self.path_in_repo.endswith(self.local_path) + else self.path_in_repo + ) + allow_patterns = [self.local_path] + ignore_patterns = [] + else: + folder_path = self.local_path + path_in_repo = self.path_in_repo + allow_patterns = self.include or [] + ignore_patterns = self.exclude or [] + if self.delete is not None and len(self.delete) > 0: + warnings.warn("Ignoring `--delete` when uploading with scheduled commits.") + + scheduler = CommitScheduler( + folder_path=folder_path, + repo_id=self.repo_id, + repo_type=self.repo_type, + revision=self.revision, + allow_patterns=allow_patterns, + ignore_patterns=ignore_patterns, + path_in_repo=path_in_repo, + private=self.private, + every=self.every, + hf_api=self.api, + ) + print(f"Scheduling commits every {self.every} minutes to {scheduler.repo_id}.") + try: # Block main thread until KeyboardInterrupt + while True: + time.sleep(100) + except KeyboardInterrupt: + scheduler.stop() + return "Stopped scheduled commits." + + # Otherwise, create repo and proceed with the upload + if not os.path.isfile(self.local_path) and not os.path.isdir(self.local_path): + raise FileNotFoundError(f"No such file or directory: '{self.local_path}'.") + repo_id = self.api.create_repo( + repo_id=self.repo_id, + repo_type=self.repo_type, + exist_ok=True, + private=self.private, + space_sdk="gradio" if self.repo_type == "space" else None, + # ^ We don't want it to fail when uploading to a Space => let's set Gradio by default. + # ^ I'd rather not add CLI args to set it explicitly as we already have `huggingface-cli repo create` for that. + ).repo_id + + # Check if branch already exists and if not, create it + if self.revision is not None and not self.create_pr: + try: + self.api.repo_info(repo_id=repo_id, repo_type=self.repo_type, revision=self.revision) + except RevisionNotFoundError: + logger.info(f"Branch '{self.revision}' not found. Creating it...") + self.api.create_branch(repo_id=repo_id, repo_type=self.repo_type, branch=self.revision, exist_ok=True) + # ^ `exist_ok=True` to avoid race concurrency issues + + # File-based upload + if os.path.isfile(self.local_path): + return self.api.upload_file( + path_or_fileobj=self.local_path, + path_in_repo=self.path_in_repo, + repo_id=repo_id, + repo_type=self.repo_type, + revision=self.revision, + commit_message=self.commit_message, + commit_description=self.commit_description, + create_pr=self.create_pr, + ) + + # Folder-based upload + else: + return self.api.upload_folder( + folder_path=self.local_path, + path_in_repo=self.path_in_repo, + repo_id=repo_id, + repo_type=self.repo_type, + revision=self.revision, + commit_message=self.commit_message, + commit_description=self.commit_description, + create_pr=self.create_pr, + allow_patterns=self.include, + ignore_patterns=self.exclude, + delete_patterns=self.delete, + ) diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/commands/upload_large_folder.py b/venv/lib/python3.13/site-packages/huggingface_hub/commands/upload_large_folder.py new file mode 100644 index 0000000000000000000000000000000000000000..3105ba3f57f5644aa18e627aa5d1d18e61515ae7 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/commands/upload_large_folder.py @@ -0,0 +1,131 @@ +# coding=utf-8 +# Copyright 2023-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains command to upload a large folder with the CLI.""" + +import os +from argparse import Namespace, _SubParsersAction +from typing import List, Optional + +from huggingface_hub import logging +from huggingface_hub.commands import BaseHuggingfaceCLICommand +from huggingface_hub.hf_api import HfApi +from huggingface_hub.utils import disable_progress_bars + +from ._cli_utils import ANSI, show_deprecation_warning + + +logger = logging.get_logger(__name__) + + +class UploadLargeFolderCommand(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction): + subparser = parser.add_parser("upload-large-folder", help="Upload a large folder to a repo on the Hub") + subparser.add_argument( + "repo_id", type=str, help="The ID of the repo to upload to (e.g. `username/repo-name`)." + ) + subparser.add_argument("local_path", type=str, help="Local path to the file or folder to upload.") + subparser.add_argument( + "--repo-type", + choices=["model", "dataset", "space"], + help="Type of the repo to upload to (e.g. `dataset`).", + ) + subparser.add_argument( + "--revision", + type=str, + help=("An optional Git revision to push to. It can be a branch name or a PR reference."), + ) + subparser.add_argument( + "--private", + action="store_true", + help=( + "Whether to create a private repo if repo doesn't exist on the Hub. Ignored if the repo already exists." + ), + ) + subparser.add_argument("--include", nargs="*", type=str, help="Glob patterns to match files to upload.") + subparser.add_argument("--exclude", nargs="*", type=str, help="Glob patterns to exclude from files to upload.") + subparser.add_argument( + "--token", type=str, help="A User Access Token generated from https://huggingface.co/settings/tokens" + ) + subparser.add_argument( + "--num-workers", type=int, help="Number of workers to use to hash, upload and commit files." + ) + subparser.add_argument("--no-report", action="store_true", help="Whether to disable regular status report.") + subparser.add_argument("--no-bars", action="store_true", help="Whether to disable progress bars.") + subparser.set_defaults(func=UploadLargeFolderCommand) + + def __init__(self, args: Namespace) -> None: + self.repo_id: str = args.repo_id + self.local_path: str = args.local_path + self.repo_type: str = args.repo_type + self.revision: Optional[str] = args.revision + self.private: bool = args.private + + self.include: Optional[List[str]] = args.include + self.exclude: Optional[List[str]] = args.exclude + + self.api: HfApi = HfApi(token=args.token, library_name="huggingface-cli") + + self.num_workers: Optional[int] = args.num_workers + self.no_report: bool = args.no_report + self.no_bars: bool = args.no_bars + + if not os.path.isdir(self.local_path): + raise ValueError("Large upload is only supported for folders.") + + def run(self) -> None: + show_deprecation_warning("huggingface-cli upload-large-folder", "hf upload-large-folder") + + logging.set_verbosity_info() + + print( + ANSI.yellow( + "You are about to upload a large folder to the Hub using `huggingface-cli upload-large-folder`. " + "This is a new feature so feedback is very welcome!\n" + "\n" + "A few things to keep in mind:\n" + " - Repository limits still apply: https://huggingface.co/docs/hub/repositories-recommendations\n" + " - Do not start several processes in parallel.\n" + " - You can interrupt and resume the process at any time. " + "The script will pick up where it left off except for partially uploaded files that would have to be entirely reuploaded.\n" + " - Do not upload the same folder to several repositories. If you need to do so, you must delete the `./.cache/huggingface/` folder first.\n" + "\n" + f"Some temporary metadata will be stored under `{self.local_path}/.cache/huggingface`.\n" + " - You must not modify those files manually.\n" + " - You must not delete the `./.cache/huggingface/` folder while a process is running.\n" + " - You can delete the `./.cache/huggingface/` folder to reinitialize the upload state when process is not running. Files will have to be hashed and preuploaded again, except for already committed files.\n" + "\n" + "If the process output is too verbose, you can disable the progress bars with `--no-bars`. " + "You can also entirely disable the status report with `--no-report`.\n" + "\n" + "For more details, run `huggingface-cli upload-large-folder --help` or check the documentation at " + "https://huggingface.co/docs/huggingface_hub/guides/upload#upload-a-large-folder." + ) + ) + + if self.no_bars: + disable_progress_bars() + + self.api.upload_large_folder( + repo_id=self.repo_id, + folder_path=self.local_path, + repo_type=self.repo_type, + revision=self.revision, + private=self.private, + allow_patterns=self.include, + ignore_patterns=self.exclude, + num_workers=self.num_workers, + print_report=not self.no_report, + ) diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/commands/user.py b/venv/lib/python3.13/site-packages/huggingface_hub/commands/user.py new file mode 100644 index 0000000000000000000000000000000000000000..3f4da0f45d0dae5bc4458f844f776db9c3971208 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/commands/user.py @@ -0,0 +1,208 @@ +# Copyright 2020 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains commands to authenticate to the Hugging Face Hub and interact with your repositories. + +Usage: + # login and save token locally. + huggingface-cli login --token=hf_*** --add-to-git-credential + + # switch between tokens + huggingface-cli auth switch + + # list all tokens + huggingface-cli auth list + + # logout from a specific token, if no token-name is provided, all tokens will be deleted from your machine. + huggingface-cli logout --token-name=your_token_name + + # find out which huggingface.co account you are logged in as + huggingface-cli whoami +""" + +from argparse import _SubParsersAction +from typing import List, Optional + +from requests.exceptions import HTTPError + +from huggingface_hub.commands import BaseHuggingfaceCLICommand +from huggingface_hub.constants import ENDPOINT +from huggingface_hub.hf_api import HfApi + +from .._login import auth_list, auth_switch, login, logout +from ..utils import get_stored_tokens, get_token, logging +from ._cli_utils import ANSI, show_deprecation_warning + + +logger = logging.get_logger(__name__) + +try: + from InquirerPy import inquirer + from InquirerPy.base.control import Choice + + _inquirer_py_available = True +except ImportError: + _inquirer_py_available = False + + +class UserCommands(BaseHuggingfaceCLICommand): + @staticmethod + def register_subcommand(parser: _SubParsersAction): + login_parser = parser.add_parser("login", help="Log in using a token from huggingface.co/settings/tokens") + login_parser.add_argument( + "--token", + type=str, + help="Token generated from https://huggingface.co/settings/tokens", + ) + login_parser.add_argument( + "--add-to-git-credential", + action="store_true", + help="Optional: Save token to git credential helper.", + ) + login_parser.set_defaults(func=lambda args: LoginCommand(args)) + whoami_parser = parser.add_parser("whoami", help="Find out which huggingface.co account you are logged in as.") + whoami_parser.set_defaults(func=lambda args: WhoamiCommand(args)) + + logout_parser = parser.add_parser("logout", help="Log out") + logout_parser.add_argument( + "--token-name", + type=str, + help="Optional: Name of the access token to log out from.", + ) + logout_parser.set_defaults(func=lambda args: LogoutCommand(args)) + + auth_parser = parser.add_parser("auth", help="Other authentication related commands") + auth_subparsers = auth_parser.add_subparsers(help="Authentication subcommands") + auth_switch_parser = auth_subparsers.add_parser("switch", help="Switch between access tokens") + auth_switch_parser.add_argument( + "--token-name", + type=str, + help="Optional: Name of the access token to switch to.", + ) + auth_switch_parser.add_argument( + "--add-to-git-credential", + action="store_true", + help="Optional: Save token to git credential helper.", + ) + auth_switch_parser.set_defaults(func=lambda args: AuthSwitchCommand(args)) + auth_list_parser = auth_subparsers.add_parser("list", help="List all stored access tokens") + auth_list_parser.set_defaults(func=lambda args: AuthListCommand(args)) + + +class BaseUserCommand: + def __init__(self, args): + self.args = args + self._api = HfApi() + + +class LoginCommand(BaseUserCommand): + def run(self): + show_deprecation_warning("huggingface-cli login", "hf auth login") + + logging.set_verbosity_info() + login( + token=self.args.token, + add_to_git_credential=self.args.add_to_git_credential, + ) + + +class LogoutCommand(BaseUserCommand): + def run(self): + show_deprecation_warning("huggingface-cli logout", "hf auth logout") + + logging.set_verbosity_info() + logout(token_name=self.args.token_name) + + +class AuthSwitchCommand(BaseUserCommand): + def run(self): + show_deprecation_warning("huggingface-cli auth switch", "hf auth switch") + + logging.set_verbosity_info() + token_name = self.args.token_name + if token_name is None: + token_name = self._select_token_name() + + if token_name is None: + print("No token name provided. Aborting.") + exit() + auth_switch(token_name, add_to_git_credential=self.args.add_to_git_credential) + + def _select_token_name(self) -> Optional[str]: + token_names = list(get_stored_tokens().keys()) + + if not token_names: + logger.error("No stored tokens found. Please login first.") + return None + + if _inquirer_py_available: + return self._select_token_name_tui(token_names) + # if inquirer is not available, use a simpler terminal UI + print("Available stored tokens:") + for i, token_name in enumerate(token_names, 1): + print(f"{i}. {token_name}") + while True: + try: + choice = input("Enter the number of the token to switch to (or 'q' to quit): ") + if choice.lower() == "q": + return None + index = int(choice) - 1 + if 0 <= index < len(token_names): + return token_names[index] + else: + print("Invalid selection. Please try again.") + except ValueError: + print("Invalid input. Please enter a number or 'q' to quit.") + + def _select_token_name_tui(self, token_names: List[str]) -> Optional[str]: + choices = [Choice(token_name, name=token_name) for token_name in token_names] + try: + return inquirer.select( + message="Select a token to switch to:", + choices=choices, + default=None, + ).execute() + except KeyboardInterrupt: + logger.info("Token selection cancelled.") + return None + + +class AuthListCommand(BaseUserCommand): + def run(self): + show_deprecation_warning("huggingface-cli auth list", "hf auth list") + + logging.set_verbosity_info() + auth_list() + + +class WhoamiCommand(BaseUserCommand): + def run(self): + show_deprecation_warning("huggingface-cli whoami", "hf auth whoami") + + token = get_token() + if token is None: + print("Not logged in") + exit() + try: + info = self._api.whoami(token) + print(info["name"]) + orgs = [org["name"] for org in info["orgs"]] + if orgs: + print(ANSI.bold("orgs: "), ",".join(orgs)) + + if ENDPOINT != "https://huggingface.co": + print(f"Authenticated through private endpoint: {ENDPOINT}") + except HTTPError as e: + print(e) + print(ANSI.red(e.response.text)) + exit(1) diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/commands/version.py b/venv/lib/python3.13/site-packages/huggingface_hub/commands/version.py new file mode 100644 index 0000000000000000000000000000000000000000..10d341bcdb93e0616fcf80370ac8dde63b15ce9c --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/commands/version.py @@ -0,0 +1,40 @@ +# Copyright 2022 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains command to print information about the version. + +Usage: + huggingface-cli version +""" + +from argparse import _SubParsersAction + +from huggingface_hub import __version__ + +from . import BaseHuggingfaceCLICommand +from ._cli_utils import show_deprecation_warning + + +class VersionCommand(BaseHuggingfaceCLICommand): + def __init__(self, args): + self.args = args + + @staticmethod + def register_subcommand(parser: _SubParsersAction): + version_parser = parser.add_parser("version", help="Print information about the huggingface-cli version.") + version_parser.set_defaults(func=VersionCommand) + + def run(self) -> None: + show_deprecation_warning("huggingface-cli version", "hf version") + + print(f"huggingface_hub version: {__version__}") diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/inference/__init__.py b/venv/lib/python3.13/site-packages/huggingface_hub/inference/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/inference/_client.py b/venv/lib/python3.13/site-packages/huggingface_hub/inference/_client.py new file mode 100644 index 0000000000000000000000000000000000000000..f50e7d56eb9fd5b0362fb60819d97ee406ded94f --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/inference/_client.py @@ -0,0 +1,3368 @@ +# coding=utf-8 +# Copyright 2023-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Related resources: +# https://huggingface.co/tasks +# https://huggingface.co/docs/huggingface.js/inference/README +# https://github.com/huggingface/huggingface.js/tree/main/packages/inference/src +# https://github.com/huggingface/text-generation-inference/tree/main/clients/python +# https://github.com/huggingface/text-generation-inference/blob/main/clients/python/text_generation/client.py +# https://huggingface.slack.com/archives/C03E4DQ9LAJ/p1680169099087869 +# https://github.com/huggingface/unity-api#tasks +# +# Some TODO: +# - add all tasks +# +# NOTE: the philosophy of this client is "let's make it as easy as possible to use it, even if less optimized". Some +# examples of how it translates: +# - Timeout / Server unavailable is handled by the client in a single "timeout" parameter. +# - Files can be provided as bytes, file paths, or URLs and the client will try to "guess" the type. +# - Images are parsed as PIL.Image for easier manipulation. +# - Provides a "recommended model" for each task => suboptimal but user-wise quicker to get a first script running. +# - Only the main parameters are publicly exposed. Power users can always read the docs for more options. +import base64 +import logging +import re +import warnings +from typing import TYPE_CHECKING, Any, Dict, Iterable, List, Literal, Optional, Union, overload + +from requests import HTTPError + +from huggingface_hub import constants +from huggingface_hub.errors import BadRequestError, InferenceTimeoutError +from huggingface_hub.inference._common import ( + TASKS_EXPECTING_IMAGES, + ContentT, + RequestParameters, + _b64_encode, + _b64_to_image, + _bytes_to_dict, + _bytes_to_image, + _bytes_to_list, + _get_unsupported_text_generation_kwargs, + _import_numpy, + _set_unsupported_text_generation_kwargs, + _stream_chat_completion_response, + _stream_text_generation_response, + raise_text_generation_error, +) +from huggingface_hub.inference._generated.types import ( + AudioClassificationOutputElement, + AudioClassificationOutputTransform, + AudioToAudioOutputElement, + AutomaticSpeechRecognitionOutput, + ChatCompletionInputGrammarType, + ChatCompletionInputMessage, + ChatCompletionInputStreamOptions, + ChatCompletionInputTool, + ChatCompletionInputToolChoiceClass, + ChatCompletionInputToolChoiceEnum, + ChatCompletionOutput, + ChatCompletionStreamOutput, + DocumentQuestionAnsweringOutputElement, + FillMaskOutputElement, + ImageClassificationOutputElement, + ImageClassificationOutputTransform, + ImageSegmentationOutputElement, + ImageSegmentationSubtask, + ImageToImageTargetSize, + ImageToTextOutput, + ImageToVideoTargetSize, + ObjectDetectionOutputElement, + Padding, + QuestionAnsweringOutputElement, + SummarizationOutput, + SummarizationTruncationStrategy, + TableQuestionAnsweringOutputElement, + TextClassificationOutputElement, + TextClassificationOutputTransform, + TextGenerationInputGrammarType, + TextGenerationOutput, + TextGenerationStreamOutput, + TextToSpeechEarlyStoppingEnum, + TokenClassificationAggregationStrategy, + TokenClassificationOutputElement, + TranslationOutput, + TranslationTruncationStrategy, + VisualQuestionAnsweringOutputElement, + ZeroShotClassificationOutputElement, + ZeroShotImageClassificationOutputElement, +) +from huggingface_hub.inference._providers import PROVIDER_OR_POLICY_T, get_provider_helper +from huggingface_hub.utils import build_hf_headers, get_session, hf_raise_for_status +from huggingface_hub.utils._auth import get_token + + +if TYPE_CHECKING: + import numpy as np + from PIL.Image import Image + +logger = logging.getLogger(__name__) + + +MODEL_KWARGS_NOT_USED_REGEX = re.compile(r"The following `model_kwargs` are not used by the model: \[(.*?)\]") + + +class InferenceClient: + """ + Initialize a new Inference Client. + + [`InferenceClient`] aims to provide a unified experience to perform inference. The client can be used + seamlessly with either the (free) Inference API, self-hosted Inference Endpoints, or third-party Inference Providers. + + Args: + model (`str`, `optional`): + The model to run inference with. Can be a model id hosted on the Hugging Face Hub, e.g. `meta-llama/Meta-Llama-3-8B-Instruct` + or a URL to a deployed Inference Endpoint. Defaults to None, in which case a recommended model is + automatically selected for the task. + Note: for better compatibility with OpenAI's client, `model` has been aliased as `base_url`. Those 2 + arguments are mutually exclusive. If a URL is passed as `model` or `base_url` for chat completion, the `(/v1)/chat/completions` suffix path will be appended to the URL. + provider (`str`, *optional*): + Name of the provider to use for inference. Can be `"black-forest-labs"`, `"cerebras"`, `"clarifai"`, `"cohere"`, `"fal-ai"`, `"featherless-ai"`, `"fireworks-ai"`, `"groq"`, `"hf-inference"`, `"hyperbolic"`, `"nebius"`, `"novita"`, `"nscale"`, `"openai"`, `publicai`, `"replicate"`, `"sambanova"`, `"scaleway"`, `"together"` or `"zai-org"`. + Defaults to "auto" i.e. the first of the providers available for the model, sorted by the user's order in https://hf.co/settings/inference-providers. + If model is a URL or `base_url` is passed, then `provider` is not used. + token (`str`, *optional*): + Hugging Face token. Will default to the locally saved token if not provided. + Note: for better compatibility with OpenAI's client, `token` has been aliased as `api_key`. Those 2 + arguments are mutually exclusive and have the exact same behavior. + timeout (`float`, `optional`): + The maximum number of seconds to wait for a response from the server. Defaults to None, meaning it will loop until the server is available. + headers (`Dict[str, str]`, `optional`): + Additional headers to send to the server. By default only the authorization and user-agent headers are sent. + Values in this dictionary will override the default values. + bill_to (`str`, `optional`): + The billing account to use for the requests. By default the requests are billed on the user's account. + Requests can only be billed to an organization the user is a member of, and which has subscribed to Enterprise Hub. + cookies (`Dict[str, str]`, `optional`): + Additional cookies to send to the server. + proxies (`Any`, `optional`): + Proxies to use for the request. + base_url (`str`, `optional`): + Base URL to run inference. This is a duplicated argument from `model` to make [`InferenceClient`] + follow the same pattern as `openai.OpenAI` client. Cannot be used if `model` is set. Defaults to None. + api_key (`str`, `optional`): + Token to use for authentication. This is a duplicated argument from `token` to make [`InferenceClient`] + follow the same pattern as `openai.OpenAI` client. Cannot be used if `token` is set. Defaults to None. + """ + + def __init__( + self, + model: Optional[str] = None, + *, + provider: Optional[PROVIDER_OR_POLICY_T] = None, + token: Optional[str] = None, + timeout: Optional[float] = None, + headers: Optional[Dict[str, str]] = None, + cookies: Optional[Dict[str, str]] = None, + proxies: Optional[Any] = None, + bill_to: Optional[str] = None, + # OpenAI compatibility + base_url: Optional[str] = None, + api_key: Optional[str] = None, + ) -> None: + if model is not None and base_url is not None: + raise ValueError( + "Received both `model` and `base_url` arguments. Please provide only one of them." + " `base_url` is an alias for `model` to make the API compatible with OpenAI's client." + " If using `base_url` for chat completion, the `/chat/completions` suffix path will be appended to the base url." + " When passing a URL as `model`, the client will not append any suffix path to it." + ) + if token is not None and api_key is not None: + raise ValueError( + "Received both `token` and `api_key` arguments. Please provide only one of them." + " `api_key` is an alias for `token` to make the API compatible with OpenAI's client." + " It has the exact same behavior as `token`." + ) + token = token if token is not None else api_key + if isinstance(token, bool): + # Legacy behavior: previously is was possible to pass `token=False` to disable authentication. This is not + # supported anymore as authentication is required. Better to explicitly raise here rather than risking + # sending the locally saved token without the user knowing about it. + if token is False: + raise ValueError( + "Cannot use `token=False` to disable authentication as authentication is required to run Inference." + ) + warnings.warn( + "Using `token=True` to automatically use the locally saved token is deprecated and will be removed in a future release. " + "Please use `token=None` instead (default).", + DeprecationWarning, + ) + token = get_token() + + self.model: Optional[str] = base_url or model + self.token: Optional[str] = token + + self.headers = {**headers} if headers is not None else {} + if bill_to is not None: + if ( + constants.HUGGINGFACE_HEADER_X_BILL_TO in self.headers + and self.headers[constants.HUGGINGFACE_HEADER_X_BILL_TO] != bill_to + ): + warnings.warn( + f"Overriding existing '{self.headers[constants.HUGGINGFACE_HEADER_X_BILL_TO]}' value in headers with '{bill_to}'.", + UserWarning, + ) + self.headers[constants.HUGGINGFACE_HEADER_X_BILL_TO] = bill_to + + if token is not None and not token.startswith("hf_"): + warnings.warn( + "You've provided an external provider's API key, so requests will be billed directly by the provider. " + "The `bill_to` parameter is only applicable for Hugging Face billing and will be ignored.", + UserWarning, + ) + + # Configure provider + self.provider = provider + + self.cookies = cookies + self.timeout = timeout + self.proxies = proxies + + def __repr__(self): + return f"" + + @overload + def _inner_post( # type: ignore[misc] + self, request_parameters: RequestParameters, *, stream: Literal[False] = ... + ) -> bytes: ... + + @overload + def _inner_post( # type: ignore[misc] + self, request_parameters: RequestParameters, *, stream: Literal[True] = ... + ) -> Iterable[bytes]: ... + + @overload + def _inner_post( + self, request_parameters: RequestParameters, *, stream: bool = False + ) -> Union[bytes, Iterable[bytes]]: ... + + def _inner_post( + self, request_parameters: RequestParameters, *, stream: bool = False + ) -> Union[bytes, Iterable[bytes]]: + """Make a request to the inference server.""" + # TODO: this should be handled in provider helpers directly + if request_parameters.task in TASKS_EXPECTING_IMAGES and "Accept" not in request_parameters.headers: + request_parameters.headers["Accept"] = "image/png" + + try: + response = get_session().post( + request_parameters.url, + json=request_parameters.json, + data=request_parameters.data, + headers=request_parameters.headers, + cookies=self.cookies, + timeout=self.timeout, + stream=stream, + proxies=self.proxies, + ) + except TimeoutError as error: + # Convert any `TimeoutError` to a `InferenceTimeoutError` + raise InferenceTimeoutError(f"Inference call timed out: {request_parameters.url}") from error # type: ignore + + try: + hf_raise_for_status(response) + return response.iter_lines() if stream else response.content + except HTTPError as error: + if error.response.status_code == 422 and request_parameters.task != "unknown": + msg = str(error.args[0]) + if len(error.response.text) > 0: + msg += f"\n{error.response.text}\n" + error.args = (msg,) + error.args[1:] + raise + + def audio_classification( + self, + audio: ContentT, + *, + model: Optional[str] = None, + top_k: Optional[int] = None, + function_to_apply: Optional["AudioClassificationOutputTransform"] = None, + ) -> List[AudioClassificationOutputElement]: + """ + Perform audio classification on the provided audio content. + + Args: + audio (Union[str, Path, bytes, BinaryIO]): + The audio content to classify. It can be raw audio bytes, a local audio file, or a URL pointing to an + audio file. + model (`str`, *optional*): + The model to use for audio classification. Can be a model ID hosted on the Hugging Face Hub + or a URL to a deployed Inference Endpoint. If not provided, the default recommended model for + audio classification will be used. + top_k (`int`, *optional*): + When specified, limits the output to the top K most probable classes. + function_to_apply (`"AudioClassificationOutputTransform"`, *optional*): + The function to apply to the model outputs in order to retrieve the scores. + + Returns: + `List[AudioClassificationOutputElement]`: List of [`AudioClassificationOutputElement`] items containing the predicted labels and their confidence. + + Raises: + [`InferenceTimeoutError`]: + If the model is unavailable or the request times out. + `HTTPError`: + If the request fails with an HTTP error status code other than HTTP 503. + + Example: + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient() + >>> client.audio_classification("audio.flac") + [ + AudioClassificationOutputElement(score=0.4976358711719513, label='hap'), + AudioClassificationOutputElement(score=0.3677836060523987, label='neu'), + ... + ] + ``` + """ + model_id = model or self.model + provider_helper = get_provider_helper(self.provider, task="audio-classification", model=model_id) + request_parameters = provider_helper.prepare_request( + inputs=audio, + parameters={"function_to_apply": function_to_apply, "top_k": top_k}, + headers=self.headers, + model=model_id, + api_key=self.token, + ) + response = self._inner_post(request_parameters) + return AudioClassificationOutputElement.parse_obj_as_list(response) + + def audio_to_audio( + self, + audio: ContentT, + *, + model: Optional[str] = None, + ) -> List[AudioToAudioOutputElement]: + """ + Performs multiple tasks related to audio-to-audio depending on the model (eg: speech enhancement, source separation). + + Args: + audio (Union[str, Path, bytes, BinaryIO]): + The audio content for the model. It can be raw audio bytes, a local audio file, or a URL pointing to an + audio file. + model (`str`, *optional*): + The model can be any model which takes an audio file and returns another audio file. Can be a model ID hosted on the Hugging Face Hub + or a URL to a deployed Inference Endpoint. If not provided, the default recommended model for + audio_to_audio will be used. + + Returns: + `List[AudioToAudioOutputElement]`: A list of [`AudioToAudioOutputElement`] items containing audios label, content-type, and audio content in blob. + + Raises: + `InferenceTimeoutError`: + If the model is unavailable or the request times out. + `HTTPError`: + If the request fails with an HTTP error status code other than HTTP 503. + + Example: + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient() + >>> audio_output = client.audio_to_audio("audio.flac") + >>> for i, item in enumerate(audio_output): + >>> with open(f"output_{i}.flac", "wb") as f: + f.write(item.blob) + ``` + """ + model_id = model or self.model + provider_helper = get_provider_helper(self.provider, task="audio-to-audio", model=model_id) + request_parameters = provider_helper.prepare_request( + inputs=audio, + parameters={}, + headers=self.headers, + model=model_id, + api_key=self.token, + ) + response = self._inner_post(request_parameters) + audio_output = AudioToAudioOutputElement.parse_obj_as_list(response) + for item in audio_output: + item.blob = base64.b64decode(item.blob) + return audio_output + + def automatic_speech_recognition( + self, + audio: ContentT, + *, + model: Optional[str] = None, + extra_body: Optional[Dict] = None, + ) -> AutomaticSpeechRecognitionOutput: + """ + Perform automatic speech recognition (ASR or audio-to-text) on the given audio content. + + Args: + audio (Union[str, Path, bytes, BinaryIO]): + The content to transcribe. It can be raw audio bytes, local audio file, or a URL to an audio file. + model (`str`, *optional*): + The model to use for ASR. Can be a model ID hosted on the Hugging Face Hub or a URL to a deployed + Inference Endpoint. If not provided, the default recommended model for ASR will be used. + extra_body (`Dict`, *optional*): + Additional provider-specific parameters to pass to the model. Refer to the provider's documentation + for supported parameters. + Returns: + [`AutomaticSpeechRecognitionOutput`]: An item containing the transcribed text and optionally the timestamp chunks. + + Raises: + [`InferenceTimeoutError`]: + If the model is unavailable or the request times out. + `HTTPError`: + If the request fails with an HTTP error status code other than HTTP 503. + + Example: + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient() + >>> client.automatic_speech_recognition("hello_world.flac").text + "hello world" + ``` + """ + model_id = model or self.model + provider_helper = get_provider_helper(self.provider, task="automatic-speech-recognition", model=model_id) + request_parameters = provider_helper.prepare_request( + inputs=audio, + parameters={**(extra_body or {})}, + headers=self.headers, + model=model_id, + api_key=self.token, + ) + response = self._inner_post(request_parameters) + return AutomaticSpeechRecognitionOutput.parse_obj_as_instance(response) + + @overload + def chat_completion( # type: ignore + self, + messages: List[Union[Dict, ChatCompletionInputMessage]], + *, + model: Optional[str] = None, + stream: Literal[False] = False, + frequency_penalty: Optional[float] = None, + logit_bias: Optional[List[float]] = None, + logprobs: Optional[bool] = None, + max_tokens: Optional[int] = None, + n: Optional[int] = None, + presence_penalty: Optional[float] = None, + response_format: Optional[ChatCompletionInputGrammarType] = None, + seed: Optional[int] = None, + stop: Optional[List[str]] = None, + stream_options: Optional[ChatCompletionInputStreamOptions] = None, + temperature: Optional[float] = None, + tool_choice: Optional[Union[ChatCompletionInputToolChoiceClass, "ChatCompletionInputToolChoiceEnum"]] = None, + tool_prompt: Optional[str] = None, + tools: Optional[List[ChatCompletionInputTool]] = None, + top_logprobs: Optional[int] = None, + top_p: Optional[float] = None, + extra_body: Optional[Dict] = None, + ) -> ChatCompletionOutput: ... + + @overload + def chat_completion( # type: ignore + self, + messages: List[Union[Dict, ChatCompletionInputMessage]], + *, + model: Optional[str] = None, + stream: Literal[True] = True, + frequency_penalty: Optional[float] = None, + logit_bias: Optional[List[float]] = None, + logprobs: Optional[bool] = None, + max_tokens: Optional[int] = None, + n: Optional[int] = None, + presence_penalty: Optional[float] = None, + response_format: Optional[ChatCompletionInputGrammarType] = None, + seed: Optional[int] = None, + stop: Optional[List[str]] = None, + stream_options: Optional[ChatCompletionInputStreamOptions] = None, + temperature: Optional[float] = None, + tool_choice: Optional[Union[ChatCompletionInputToolChoiceClass, "ChatCompletionInputToolChoiceEnum"]] = None, + tool_prompt: Optional[str] = None, + tools: Optional[List[ChatCompletionInputTool]] = None, + top_logprobs: Optional[int] = None, + top_p: Optional[float] = None, + extra_body: Optional[Dict] = None, + ) -> Iterable[ChatCompletionStreamOutput]: ... + + @overload + def chat_completion( + self, + messages: List[Union[Dict, ChatCompletionInputMessage]], + *, + model: Optional[str] = None, + stream: bool = False, + frequency_penalty: Optional[float] = None, + logit_bias: Optional[List[float]] = None, + logprobs: Optional[bool] = None, + max_tokens: Optional[int] = None, + n: Optional[int] = None, + presence_penalty: Optional[float] = None, + response_format: Optional[ChatCompletionInputGrammarType] = None, + seed: Optional[int] = None, + stop: Optional[List[str]] = None, + stream_options: Optional[ChatCompletionInputStreamOptions] = None, + temperature: Optional[float] = None, + tool_choice: Optional[Union[ChatCompletionInputToolChoiceClass, "ChatCompletionInputToolChoiceEnum"]] = None, + tool_prompt: Optional[str] = None, + tools: Optional[List[ChatCompletionInputTool]] = None, + top_logprobs: Optional[int] = None, + top_p: Optional[float] = None, + extra_body: Optional[Dict] = None, + ) -> Union[ChatCompletionOutput, Iterable[ChatCompletionStreamOutput]]: ... + + def chat_completion( + self, + messages: List[Union[Dict, ChatCompletionInputMessage]], + *, + model: Optional[str] = None, + stream: bool = False, + # Parameters from ChatCompletionInput (handled manually) + frequency_penalty: Optional[float] = None, + logit_bias: Optional[List[float]] = None, + logprobs: Optional[bool] = None, + max_tokens: Optional[int] = None, + n: Optional[int] = None, + presence_penalty: Optional[float] = None, + response_format: Optional[ChatCompletionInputGrammarType] = None, + seed: Optional[int] = None, + stop: Optional[List[str]] = None, + stream_options: Optional[ChatCompletionInputStreamOptions] = None, + temperature: Optional[float] = None, + tool_choice: Optional[Union[ChatCompletionInputToolChoiceClass, "ChatCompletionInputToolChoiceEnum"]] = None, + tool_prompt: Optional[str] = None, + tools: Optional[List[ChatCompletionInputTool]] = None, + top_logprobs: Optional[int] = None, + top_p: Optional[float] = None, + extra_body: Optional[Dict] = None, + ) -> Union[ChatCompletionOutput, Iterable[ChatCompletionStreamOutput]]: + """ + A method for completing conversations using a specified language model. + + > [!TIP] + > The `client.chat_completion` method is aliased as `client.chat.completions.create` for compatibility with OpenAI's client. + > Inputs and outputs are strictly the same and using either syntax will yield the same results. + > Check out the [Inference guide](https://huggingface.co/docs/huggingface_hub/guides/inference#openai-compatibility) + > for more details about OpenAI's compatibility. + + > [!TIP] + > You can pass provider-specific parameters to the model by using the `extra_body` argument. + + Args: + messages (List of [`ChatCompletionInputMessage`]): + Conversation history consisting of roles and content pairs. + model (`str`, *optional*): + The model to use for chat-completion. Can be a model ID hosted on the Hugging Face Hub or a URL to a deployed + Inference Endpoint. If not provided, the default recommended model for chat-based text-generation will be used. + See https://huggingface.co/tasks/text-generation for more details. + If `model` is a model ID, it is passed to the server as the `model` parameter. If you want to define a + custom URL while setting `model` in the request payload, you must set `base_url` when initializing [`InferenceClient`]. + frequency_penalty (`float`, *optional*): + Penalizes new tokens based on their existing frequency + in the text so far. Range: [-2.0, 2.0]. Defaults to 0.0. + logit_bias (`List[float]`, *optional*): + Adjusts the likelihood of specific tokens appearing in the generated output. + logprobs (`bool`, *optional*): + Whether to return log probabilities of the output tokens or not. If true, returns the log + probabilities of each output token returned in the content of message. + max_tokens (`int`, *optional*): + Maximum number of tokens allowed in the response. Defaults to 100. + n (`int`, *optional*): + The number of completions to generate for each prompt. + presence_penalty (`float`, *optional*): + Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the + text so far, increasing the model's likelihood to talk about new topics. + response_format ([`ChatCompletionInputGrammarType`], *optional*): + Grammar constraints. Can be either a JSONSchema or a regex. + seed (Optional[`int`], *optional*): + Seed for reproducible control flow. Defaults to None. + stop (`List[str]`, *optional*): + Up to four strings which trigger the end of the response. + Defaults to None. + stream (`bool`, *optional*): + Enable realtime streaming of responses. Defaults to False. + stream_options ([`ChatCompletionInputStreamOptions`], *optional*): + Options for streaming completions. + temperature (`float`, *optional*): + Controls randomness of the generations. Lower values ensure + less random completions. Range: [0, 2]. Defaults to 1.0. + top_logprobs (`int`, *optional*): + An integer between 0 and 5 specifying the number of most likely tokens to return at each token + position, each with an associated log probability. logprobs must be set to true if this parameter is + used. + top_p (`float`, *optional*): + Fraction of the most likely next words to sample from. + Must be between 0 and 1. Defaults to 1.0. + tool_choice ([`ChatCompletionInputToolChoiceClass`] or [`ChatCompletionInputToolChoiceEnum`], *optional*): + The tool to use for the completion. Defaults to "auto". + tool_prompt (`str`, *optional*): + A prompt to be appended before the tools. + tools (List of [`ChatCompletionInputTool`], *optional*): + A list of tools the model may call. Currently, only functions are supported as a tool. Use this to + provide a list of functions the model may generate JSON inputs for. + extra_body (`Dict`, *optional*): + Additional provider-specific parameters to pass to the model. Refer to the provider's documentation + for supported parameters. + Returns: + [`ChatCompletionOutput`] or Iterable of [`ChatCompletionStreamOutput`]: + Generated text returned from the server: + - if `stream=False`, the generated text is returned as a [`ChatCompletionOutput`] (default). + - if `stream=True`, the generated text is returned token by token as a sequence of [`ChatCompletionStreamOutput`]. + + Raises: + [`InferenceTimeoutError`]: + If the model is unavailable or the request times out. + `HTTPError`: + If the request fails with an HTTP error status code other than HTTP 503. + + Example: + + ```py + >>> from huggingface_hub import InferenceClient + >>> messages = [{"role": "user", "content": "What is the capital of France?"}] + >>> client = InferenceClient("meta-llama/Meta-Llama-3-8B-Instruct") + >>> client.chat_completion(messages, max_tokens=100) + ChatCompletionOutput( + choices=[ + ChatCompletionOutputComplete( + finish_reason='eos_token', + index=0, + message=ChatCompletionOutputMessage( + role='assistant', + content='The capital of France is Paris.', + name=None, + tool_calls=None + ), + logprobs=None + ) + ], + created=1719907176, + id='', + model='meta-llama/Meta-Llama-3-8B-Instruct', + object='text_completion', + system_fingerprint='2.0.4-sha-f426a33', + usage=ChatCompletionOutputUsage( + completion_tokens=8, + prompt_tokens=17, + total_tokens=25 + ) + ) + ``` + + Example using streaming: + ```py + >>> from huggingface_hub import InferenceClient + >>> messages = [{"role": "user", "content": "What is the capital of France?"}] + >>> client = InferenceClient("meta-llama/Meta-Llama-3-8B-Instruct") + >>> for token in client.chat_completion(messages, max_tokens=10, stream=True): + ... print(token) + ChatCompletionStreamOutput(choices=[ChatCompletionStreamOutputChoice(delta=ChatCompletionStreamOutputDelta(content='The', role='assistant'), index=0, finish_reason=None)], created=1710498504) + ChatCompletionStreamOutput(choices=[ChatCompletionStreamOutputChoice(delta=ChatCompletionStreamOutputDelta(content=' capital', role='assistant'), index=0, finish_reason=None)], created=1710498504) + (...) + ChatCompletionStreamOutput(choices=[ChatCompletionStreamOutputChoice(delta=ChatCompletionStreamOutputDelta(content=' may', role='assistant'), index=0, finish_reason=None)], created=1710498504) + ``` + + Example using OpenAI's syntax: + ```py + # instead of `from openai import OpenAI` + from huggingface_hub import InferenceClient + + # instead of `client = OpenAI(...)` + client = InferenceClient( + base_url=..., + api_key=..., + ) + + output = client.chat.completions.create( + model="meta-llama/Meta-Llama-3-8B-Instruct", + messages=[ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": "Count to 10"}, + ], + stream=True, + max_tokens=1024, + ) + + for chunk in output: + print(chunk.choices[0].delta.content) + ``` + + Example using a third-party provider directly with extra (provider-specific) parameters. Usage will be billed on your Together AI account. + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient( + ... provider="together", # Use Together AI provider + ... api_key="", # Pass your Together API key directly + ... ) + >>> client.chat_completion( + ... model="meta-llama/Meta-Llama-3-8B-Instruct", + ... messages=[{"role": "user", "content": "What is the capital of France?"}], + ... extra_body={"safety_model": "Meta-Llama/Llama-Guard-7b"}, + ... ) + ``` + + Example using a third-party provider through Hugging Face Routing. Usage will be billed on your Hugging Face account. + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient( + ... provider="sambanova", # Use Sambanova provider + ... api_key="hf_...", # Pass your HF token + ... ) + >>> client.chat_completion( + ... model="meta-llama/Meta-Llama-3-8B-Instruct", + ... messages=[{"role": "user", "content": "What is the capital of France?"}], + ... ) + ``` + + Example using Image + Text as input: + ```py + >>> from huggingface_hub import InferenceClient + + # provide a remote URL + >>> image_url ="https://cdn.britannica.com/61/93061-050-99147DCE/Statue-of-Liberty-Island-New-York-Bay.jpg" + # or a base64-encoded image + >>> image_path = "/path/to/image.jpeg" + >>> with open(image_path, "rb") as f: + ... base64_image = base64.b64encode(f.read()).decode("utf-8") + >>> image_url = f"data:image/jpeg;base64,{base64_image}" + + >>> client = InferenceClient("meta-llama/Llama-3.2-11B-Vision-Instruct") + >>> output = client.chat.completions.create( + ... messages=[ + ... { + ... "role": "user", + ... "content": [ + ... { + ... "type": "image_url", + ... "image_url": {"url": image_url}, + ... }, + ... { + ... "type": "text", + ... "text": "Describe this image in one sentence.", + ... }, + ... ], + ... }, + ... ], + ... ) + >>> output + The image depicts the iconic Statue of Liberty situated in New York Harbor, New York, on a clear day. + ``` + + Example using tools: + ```py + >>> client = InferenceClient("meta-llama/Meta-Llama-3-70B-Instruct") + >>> messages = [ + ... { + ... "role": "system", + ... "content": "Don't make assumptions about what values to plug into functions. Ask for clarification if a user request is ambiguous.", + ... }, + ... { + ... "role": "user", + ... "content": "What's the weather like the next 3 days in San Francisco, CA?", + ... }, + ... ] + >>> tools = [ + ... { + ... "type": "function", + ... "function": { + ... "name": "get_current_weather", + ... "description": "Get the current weather", + ... "parameters": { + ... "type": "object", + ... "properties": { + ... "location": { + ... "type": "string", + ... "description": "The city and state, e.g. San Francisco, CA", + ... }, + ... "format": { + ... "type": "string", + ... "enum": ["celsius", "fahrenheit"], + ... "description": "The temperature unit to use. Infer this from the users location.", + ... }, + ... }, + ... "required": ["location", "format"], + ... }, + ... }, + ... }, + ... { + ... "type": "function", + ... "function": { + ... "name": "get_n_day_weather_forecast", + ... "description": "Get an N-day weather forecast", + ... "parameters": { + ... "type": "object", + ... "properties": { + ... "location": { + ... "type": "string", + ... "description": "The city and state, e.g. San Francisco, CA", + ... }, + ... "format": { + ... "type": "string", + ... "enum": ["celsius", "fahrenheit"], + ... "description": "The temperature unit to use. Infer this from the users location.", + ... }, + ... "num_days": { + ... "type": "integer", + ... "description": "The number of days to forecast", + ... }, + ... }, + ... "required": ["location", "format", "num_days"], + ... }, + ... }, + ... }, + ... ] + + >>> response = client.chat_completion( + ... model="meta-llama/Meta-Llama-3-70B-Instruct", + ... messages=messages, + ... tools=tools, + ... tool_choice="auto", + ... max_tokens=500, + ... ) + >>> response.choices[0].message.tool_calls[0].function + ChatCompletionOutputFunctionDefinition( + arguments={ + 'location': 'San Francisco, CA', + 'format': 'fahrenheit', + 'num_days': 3 + }, + name='get_n_day_weather_forecast', + description=None + ) + ``` + + Example using response_format: + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient("meta-llama/Meta-Llama-3-70B-Instruct") + >>> messages = [ + ... { + ... "role": "user", + ... "content": "I saw a puppy a cat and a raccoon during my bike ride in the park. What did I saw and when?", + ... }, + ... ] + >>> response_format = { + ... "type": "json", + ... "value": { + ... "properties": { + ... "location": {"type": "string"}, + ... "activity": {"type": "string"}, + ... "animals_seen": {"type": "integer", "minimum": 1, "maximum": 5}, + ... "animals": {"type": "array", "items": {"type": "string"}}, + ... }, + ... "required": ["location", "activity", "animals_seen", "animals"], + ... }, + ... } + >>> response = client.chat_completion( + ... messages=messages, + ... response_format=response_format, + ... max_tokens=500, + ... ) + >>> response.choices[0].message.content + '{\n\n"activity": "bike ride",\n"animals": ["puppy", "cat", "raccoon"],\n"animals_seen": 3,\n"location": "park"}' + ``` + """ + # Since `chat_completion(..., model=xxx)` is also a payload parameter for the server, we need to handle 'model' differently. + # `self.model` takes precedence over 'model' argument for building URL. + # `model` takes precedence for payload value. + model_id_or_url = self.model or model + payload_model = model or self.model + + # Get the provider helper + provider_helper = get_provider_helper( + self.provider, + task="conversational", + model=model_id_or_url + if model_id_or_url is not None and model_id_or_url.startswith(("http://", "https://")) + else payload_model, + ) + + # Prepare the payload + parameters = { + "model": payload_model, + "frequency_penalty": frequency_penalty, + "logit_bias": logit_bias, + "logprobs": logprobs, + "max_tokens": max_tokens, + "n": n, + "presence_penalty": presence_penalty, + "response_format": response_format, + "seed": seed, + "stop": stop, + "temperature": temperature, + "tool_choice": tool_choice, + "tool_prompt": tool_prompt, + "tools": tools, + "top_logprobs": top_logprobs, + "top_p": top_p, + "stream": stream, + "stream_options": stream_options, + **(extra_body or {}), + } + request_parameters = provider_helper.prepare_request( + inputs=messages, + parameters=parameters, + headers=self.headers, + model=model_id_or_url, + api_key=self.token, + ) + data = self._inner_post(request_parameters, stream=stream) + + if stream: + return _stream_chat_completion_response(data) # type: ignore[arg-type] + + return ChatCompletionOutput.parse_obj_as_instance(data) # type: ignore[arg-type] + + def document_question_answering( + self, + image: ContentT, + question: str, + *, + model: Optional[str] = None, + doc_stride: Optional[int] = None, + handle_impossible_answer: Optional[bool] = None, + lang: Optional[str] = None, + max_answer_len: Optional[int] = None, + max_question_len: Optional[int] = None, + max_seq_len: Optional[int] = None, + top_k: Optional[int] = None, + word_boxes: Optional[List[Union[List[float], str]]] = None, + ) -> List[DocumentQuestionAnsweringOutputElement]: + """ + Answer questions on document images. + + Args: + image (`Union[str, Path, bytes, BinaryIO]`): + The input image for the context. It can be raw bytes, an image file, or a URL to an online image. + question (`str`): + Question to be answered. + model (`str`, *optional*): + The model to use for the document question answering task. Can be a model ID hosted on the Hugging Face Hub or a URL to + a deployed Inference Endpoint. If not provided, the default recommended document question answering model will be used. + Defaults to None. + doc_stride (`int`, *optional*): + If the words in the document are too long to fit with the question for the model, it will be split in + several chunks with some overlap. This argument controls the size of that overlap. + handle_impossible_answer (`bool`, *optional*): + Whether to accept impossible as an answer + lang (`str`, *optional*): + Language to use while running OCR. Defaults to english. + max_answer_len (`int`, *optional*): + The maximum length of predicted answers (e.g., only answers with a shorter length are considered). + max_question_len (`int`, *optional*): + The maximum length of the question after tokenization. It will be truncated if needed. + max_seq_len (`int`, *optional*): + The maximum length of the total sentence (context + question) in tokens of each chunk passed to the + model. The context will be split in several chunks (using doc_stride as overlap) if needed. + top_k (`int`, *optional*): + The number of answers to return (will be chosen by order of likelihood). Can return less than top_k + answers if there are not enough options available within the context. + word_boxes (`List[Union[List[float], str`, *optional*): + A list of words and bounding boxes (normalized 0->1000). If provided, the inference will skip the OCR + step and use the provided bounding boxes instead. + Returns: + `List[DocumentQuestionAnsweringOutputElement]`: a list of [`DocumentQuestionAnsweringOutputElement`] items containing the predicted label, associated probability, word ids, and page number. + + Raises: + [`InferenceTimeoutError`]: + If the model is unavailable or the request times out. + `HTTPError`: + If the request fails with an HTTP error status code other than HTTP 503. + + + Example: + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient() + >>> client.document_question_answering(image="https://huggingface.co/spaces/impira/docquery/resolve/2359223c1837a7587402bda0f2643382a6eefeab/invoice.png", question="What is the invoice number?") + [DocumentQuestionAnsweringOutputElement(answer='us-001', end=16, score=0.9999666213989258, start=16)] + ``` + """ + model_id = model or self.model + provider_helper = get_provider_helper(self.provider, task="document-question-answering", model=model_id) + inputs: Dict[str, Any] = {"question": question, "image": _b64_encode(image)} + request_parameters = provider_helper.prepare_request( + inputs=inputs, + parameters={ + "doc_stride": doc_stride, + "handle_impossible_answer": handle_impossible_answer, + "lang": lang, + "max_answer_len": max_answer_len, + "max_question_len": max_question_len, + "max_seq_len": max_seq_len, + "top_k": top_k, + "word_boxes": word_boxes, + }, + headers=self.headers, + model=model_id, + api_key=self.token, + ) + response = self._inner_post(request_parameters) + return DocumentQuestionAnsweringOutputElement.parse_obj_as_list(response) + + def feature_extraction( + self, + text: str, + *, + normalize: Optional[bool] = None, + prompt_name: Optional[str] = None, + truncate: Optional[bool] = None, + truncation_direction: Optional[Literal["Left", "Right"]] = None, + model: Optional[str] = None, + ) -> "np.ndarray": + """ + Generate embeddings for a given text. + + Args: + text (`str`): + The text to embed. + model (`str`, *optional*): + The model to use for the feature extraction task. Can be a model ID hosted on the Hugging Face Hub or a URL to + a deployed Inference Endpoint. If not provided, the default recommended feature extraction model will be used. + Defaults to None. + normalize (`bool`, *optional*): + Whether to normalize the embeddings or not. + Only available on server powered by Text-Embedding-Inference. + prompt_name (`str`, *optional*): + The name of the prompt that should be used by for encoding. If not set, no prompt will be applied. + Must be a key in the `Sentence Transformers` configuration `prompts` dictionary. + For example if ``prompt_name`` is "query" and the ``prompts`` is {"query": "query: ",...}, + then the sentence "What is the capital of France?" will be encoded as "query: What is the capital of France?" + because the prompt text will be prepended before any text to encode. + truncate (`bool`, *optional*): + Whether to truncate the embeddings or not. + Only available on server powered by Text-Embedding-Inference. + truncation_direction (`Literal["Left", "Right"]`, *optional*): + Which side of the input should be truncated when `truncate=True` is passed. + + Returns: + `np.ndarray`: The embedding representing the input text as a float32 numpy array. + + Raises: + [`InferenceTimeoutError`]: + If the model is unavailable or the request times out. + `HTTPError`: + If the request fails with an HTTP error status code other than HTTP 503. + + Example: + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient() + >>> client.feature_extraction("Hi, who are you?") + array([[ 2.424802 , 2.93384 , 1.1750331 , ..., 1.240499, -0.13776633, -0.7889173 ], + [-0.42943227, -0.6364878 , -1.693462 , ..., 0.41978157, -2.4336355 , 0.6162071 ], + ..., + [ 0.28552425, -0.928395 , -1.2077185 , ..., 0.76810825, -2.1069427 , 0.6236161 ]], dtype=float32) + ``` + """ + model_id = model or self.model + provider_helper = get_provider_helper(self.provider, task="feature-extraction", model=model_id) + request_parameters = provider_helper.prepare_request( + inputs=text, + parameters={ + "normalize": normalize, + "prompt_name": prompt_name, + "truncate": truncate, + "truncation_direction": truncation_direction, + }, + headers=self.headers, + model=model_id, + api_key=self.token, + ) + response = self._inner_post(request_parameters) + np = _import_numpy() + return np.array(provider_helper.get_response(response), dtype="float32") + + def fill_mask( + self, + text: str, + *, + model: Optional[str] = None, + targets: Optional[List[str]] = None, + top_k: Optional[int] = None, + ) -> List[FillMaskOutputElement]: + """ + Fill in a hole with a missing word (token to be precise). + + Args: + text (`str`): + a string to be filled from, must contain the [MASK] token (check model card for exact name of the mask). + model (`str`, *optional*): + The model to use for the fill mask task. Can be a model ID hosted on the Hugging Face Hub or a URL to + a deployed Inference Endpoint. If not provided, the default recommended fill mask model will be used. + targets (`List[str`, *optional*): + When passed, the model will limit the scores to the passed targets instead of looking up in the whole + vocabulary. If the provided targets are not in the model vocab, they will be tokenized and the first + resulting token will be used (with a warning, and that might be slower). + top_k (`int`, *optional*): + When passed, overrides the number of predictions to return. + Returns: + `List[FillMaskOutputElement]`: a list of [`FillMaskOutputElement`] items containing the predicted label, associated + probability, token reference, and completed text. + + Raises: + [`InferenceTimeoutError`]: + If the model is unavailable or the request times out. + `HTTPError`: + If the request fails with an HTTP error status code other than HTTP 503. + + Example: + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient() + >>> client.fill_mask("The goal of life is .") + [ + FillMaskOutputElement(score=0.06897063553333282, token=11098, token_str=' happiness', sequence='The goal of life is happiness.'), + FillMaskOutputElement(score=0.06554922461509705, token=45075, token_str=' immortality', sequence='The goal of life is immortality.') + ] + ``` + """ + model_id = model or self.model + provider_helper = get_provider_helper(self.provider, task="fill-mask", model=model_id) + request_parameters = provider_helper.prepare_request( + inputs=text, + parameters={"targets": targets, "top_k": top_k}, + headers=self.headers, + model=model_id, + api_key=self.token, + ) + response = self._inner_post(request_parameters) + return FillMaskOutputElement.parse_obj_as_list(response) + + def image_classification( + self, + image: ContentT, + *, + model: Optional[str] = None, + function_to_apply: Optional["ImageClassificationOutputTransform"] = None, + top_k: Optional[int] = None, + ) -> List[ImageClassificationOutputElement]: + """ + Perform image classification on the given image using the specified model. + + Args: + image (`Union[str, Path, bytes, BinaryIO, PIL.Image.Image]`): + The image to classify. It can be raw bytes, an image file, a URL to an online image, or a PIL Image. + model (`str`, *optional*): + The model to use for image classification. Can be a model ID hosted on the Hugging Face Hub or a URL to a + deployed Inference Endpoint. If not provided, the default recommended model for image classification will be used. + function_to_apply (`"ImageClassificationOutputTransform"`, *optional*): + The function to apply to the model outputs in order to retrieve the scores. + top_k (`int`, *optional*): + When specified, limits the output to the top K most probable classes. + Returns: + `List[ImageClassificationOutputElement]`: a list of [`ImageClassificationOutputElement`] items containing the predicted label and associated probability. + + Raises: + [`InferenceTimeoutError`]: + If the model is unavailable or the request times out. + `HTTPError`: + If the request fails with an HTTP error status code other than HTTP 503. + + Example: + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient() + >>> client.image_classification("https://upload.wikimedia.org/wikipedia/commons/thumb/4/43/Cute_dog.jpg/320px-Cute_dog.jpg") + [ImageClassificationOutputElement(label='Blenheim spaniel', score=0.9779096841812134), ...] + ``` + """ + model_id = model or self.model + provider_helper = get_provider_helper(self.provider, task="image-classification", model=model_id) + request_parameters = provider_helper.prepare_request( + inputs=image, + parameters={"function_to_apply": function_to_apply, "top_k": top_k}, + headers=self.headers, + model=model_id, + api_key=self.token, + ) + response = self._inner_post(request_parameters) + return ImageClassificationOutputElement.parse_obj_as_list(response) + + def image_segmentation( + self, + image: ContentT, + *, + model: Optional[str] = None, + mask_threshold: Optional[float] = None, + overlap_mask_area_threshold: Optional[float] = None, + subtask: Optional["ImageSegmentationSubtask"] = None, + threshold: Optional[float] = None, + ) -> List[ImageSegmentationOutputElement]: + """ + Perform image segmentation on the given image using the specified model. + + > [!WARNING] + > You must have `PIL` installed if you want to work with images (`pip install Pillow`). + + Args: + image (`Union[str, Path, bytes, BinaryIO, PIL.Image.Image]`): + The image to segment. It can be raw bytes, an image file, a URL to an online image, or a PIL Image. + model (`str`, *optional*): + The model to use for image segmentation. Can be a model ID hosted on the Hugging Face Hub or a URL to a + deployed Inference Endpoint. If not provided, the default recommended model for image segmentation will be used. + mask_threshold (`float`, *optional*): + Threshold to use when turning the predicted masks into binary values. + overlap_mask_area_threshold (`float`, *optional*): + Mask overlap threshold to eliminate small, disconnected segments. + subtask (`"ImageSegmentationSubtask"`, *optional*): + Segmentation task to be performed, depending on model capabilities. + threshold (`float`, *optional*): + Probability threshold to filter out predicted masks. + Returns: + `List[ImageSegmentationOutputElement]`: A list of [`ImageSegmentationOutputElement`] items containing the segmented masks and associated attributes. + + Raises: + [`InferenceTimeoutError`]: + If the model is unavailable or the request times out. + `HTTPError`: + If the request fails with an HTTP error status code other than HTTP 503. + + Example: + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient() + >>> client.image_segmentation("cat.jpg") + [ImageSegmentationOutputElement(score=0.989008, label='LABEL_184', mask=), ...] + ``` + """ + model_id = model or self.model + provider_helper = get_provider_helper(self.provider, task="image-segmentation", model=model_id) + request_parameters = provider_helper.prepare_request( + inputs=image, + parameters={ + "mask_threshold": mask_threshold, + "overlap_mask_area_threshold": overlap_mask_area_threshold, + "subtask": subtask, + "threshold": threshold, + }, + headers=self.headers, + model=model_id, + api_key=self.token, + ) + response = self._inner_post(request_parameters) + output = ImageSegmentationOutputElement.parse_obj_as_list(response) + for item in output: + item.mask = _b64_to_image(item.mask) # type: ignore [assignment] + return output + + def image_to_image( + self, + image: ContentT, + prompt: Optional[str] = None, + *, + negative_prompt: Optional[str] = None, + num_inference_steps: Optional[int] = None, + guidance_scale: Optional[float] = None, + model: Optional[str] = None, + target_size: Optional[ImageToImageTargetSize] = None, + **kwargs, + ) -> "Image": + """ + Perform image-to-image translation using a specified model. + + > [!WARNING] + > You must have `PIL` installed if you want to work with images (`pip install Pillow`). + + Args: + image (`Union[str, Path, bytes, BinaryIO, PIL.Image.Image]`): + The input image for translation. It can be raw bytes, an image file, a URL to an online image, or a PIL Image. + prompt (`str`, *optional*): + The text prompt to guide the image generation. + negative_prompt (`str`, *optional*): + One prompt to guide what NOT to include in image generation. + num_inference_steps (`int`, *optional*): + For diffusion models. The number of denoising steps. More denoising steps usually lead to a higher + quality image at the expense of slower inference. + guidance_scale (`float`, *optional*): + For diffusion models. A higher guidance scale value encourages the model to generate images closely + linked to the text prompt at the expense of lower image quality. + model (`str`, *optional*): + The model to use for inference. Can be a model ID hosted on the Hugging Face Hub or a URL to a deployed + Inference Endpoint. This parameter overrides the model defined at the instance level. Defaults to None. + target_size (`ImageToImageTargetSize`, *optional*): + The size in pixels of the output image. This parameter is only supported by some providers and for + specific models. It will be ignored when unsupported. + + Returns: + `Image`: The translated image. + + Raises: + [`InferenceTimeoutError`]: + If the model is unavailable or the request times out. + `HTTPError`: + If the request fails with an HTTP error status code other than HTTP 503. + + Example: + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient() + >>> image = client.image_to_image("cat.jpg", prompt="turn the cat into a tiger") + >>> image.save("tiger.jpg") + ``` + """ + model_id = model or self.model + provider_helper = get_provider_helper(self.provider, task="image-to-image", model=model_id) + request_parameters = provider_helper.prepare_request( + inputs=image, + parameters={ + "prompt": prompt, + "negative_prompt": negative_prompt, + "target_size": target_size, + "num_inference_steps": num_inference_steps, + "guidance_scale": guidance_scale, + **kwargs, + }, + headers=self.headers, + model=model_id, + api_key=self.token, + ) + response = self._inner_post(request_parameters) + response = provider_helper.get_response(response, request_parameters) + return _bytes_to_image(response) + + def image_to_video( + self, + image: ContentT, + *, + model: Optional[str] = None, + prompt: Optional[str] = None, + negative_prompt: Optional[str] = None, + num_frames: Optional[float] = None, + num_inference_steps: Optional[int] = None, + guidance_scale: Optional[float] = None, + seed: Optional[int] = None, + target_size: Optional[ImageToVideoTargetSize] = None, + **kwargs, + ) -> bytes: + """ + Generate a video from an input image. + + Args: + image (`Union[str, Path, bytes, BinaryIO, PIL.Image.Image]`): + The input image to generate a video from. It can be raw bytes, an image file, a URL to an online image, or a PIL Image. + model (`str`, *optional*): + The model to use for inference. Can be a model ID hosted on the Hugging Face Hub or a URL to a deployed + Inference Endpoint. This parameter overrides the model defined at the instance level. Defaults to None. + prompt (`str`, *optional*): + The text prompt to guide the video generation. + negative_prompt (`str`, *optional*): + One prompt to guide what NOT to include in video generation. + num_frames (`float`, *optional*): + The num_frames parameter determines how many video frames are generated. + num_inference_steps (`int`, *optional*): + For diffusion models. The number of denoising steps. More denoising steps usually lead to a higher + quality image at the expense of slower inference. + guidance_scale (`float`, *optional*): + For diffusion models. A higher guidance scale value encourages the model to generate videos closely + linked to the text prompt at the expense of lower image quality. + seed (`int`, *optional*): + The seed to use for the video generation. + target_size (`ImageToVideoTargetSize`, *optional*): + The size in pixel of the output video frames. + num_inference_steps (`int`, *optional*): + The number of denoising steps. More denoising steps usually lead to a higher quality video at the + expense of slower inference. + seed (`int`, *optional*): + Seed for the random number generator. + + Returns: + `bytes`: The generated video. + + Examples: + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient() + >>> video = client.image_to_video("cat.jpg", model="Wan-AI/Wan2.2-I2V-A14B", prompt="turn the cat into a tiger") + >>> with open("tiger.mp4", "wb") as f: + ... f.write(video) + ``` + """ + model_id = model or self.model + provider_helper = get_provider_helper(self.provider, task="image-to-video", model=model_id) + request_parameters = provider_helper.prepare_request( + inputs=image, + parameters={ + "prompt": prompt, + "negative_prompt": negative_prompt, + "num_frames": num_frames, + "num_inference_steps": num_inference_steps, + "guidance_scale": guidance_scale, + "seed": seed, + "target_size": target_size, + **kwargs, + }, + headers=self.headers, + model=model_id, + api_key=self.token, + ) + response = self._inner_post(request_parameters) + response = provider_helper.get_response(response, request_parameters) + return response + + def image_to_text(self, image: ContentT, *, model: Optional[str] = None) -> ImageToTextOutput: + """ + Takes an input image and return text. + + Models can have very different outputs depending on your use case (image captioning, optical character recognition + (OCR), Pix2Struct, etc). Please have a look to the model card to learn more about a model's specificities. + + Args: + image (`Union[str, Path, bytes, BinaryIO, PIL.Image.Image]`): + The input image to caption. It can be raw bytes, an image file, a URL to an online image, or a PIL Image. + model (`str`, *optional*): + The model to use for inference. Can be a model ID hosted on the Hugging Face Hub or a URL to a deployed + Inference Endpoint. This parameter overrides the model defined at the instance level. Defaults to None. + + Returns: + [`ImageToTextOutput`]: The generated text. + + Raises: + [`InferenceTimeoutError`]: + If the model is unavailable or the request times out. + `HTTPError`: + If the request fails with an HTTP error status code other than HTTP 503. + + Example: + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient() + >>> client.image_to_text("cat.jpg") + 'a cat standing in a grassy field ' + >>> client.image_to_text("https://upload.wikimedia.org/wikipedia/commons/thumb/4/43/Cute_dog.jpg/320px-Cute_dog.jpg") + 'a dog laying on the grass next to a flower pot ' + ``` + """ + model_id = model or self.model + provider_helper = get_provider_helper(self.provider, task="image-to-text", model=model_id) + request_parameters = provider_helper.prepare_request( + inputs=image, + parameters={}, + headers=self.headers, + model=model_id, + api_key=self.token, + ) + response = self._inner_post(request_parameters) + output_list: List[ImageToTextOutput] = ImageToTextOutput.parse_obj_as_list(response) + return output_list[0] + + def object_detection( + self, image: ContentT, *, model: Optional[str] = None, threshold: Optional[float] = None + ) -> List[ObjectDetectionOutputElement]: + """ + Perform object detection on the given image using the specified model. + + > [!WARNING] + > You must have `PIL` installed if you want to work with images (`pip install Pillow`). + + Args: + image (`Union[str, Path, bytes, BinaryIO, PIL.Image.Image]`): + The image to detect objects on. It can be raw bytes, an image file, a URL to an online image, or a PIL Image. + model (`str`, *optional*): + The model to use for object detection. Can be a model ID hosted on the Hugging Face Hub or a URL to a + deployed Inference Endpoint. If not provided, the default recommended model for object detection (DETR) will be used. + threshold (`float`, *optional*): + The probability necessary to make a prediction. + Returns: + `List[ObjectDetectionOutputElement]`: A list of [`ObjectDetectionOutputElement`] items containing the bounding boxes and associated attributes. + + Raises: + [`InferenceTimeoutError`]: + If the model is unavailable or the request times out. + `HTTPError`: + If the request fails with an HTTP error status code other than HTTP 503. + `ValueError`: + If the request output is not a List. + + Example: + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient() + >>> client.object_detection("people.jpg") + [ObjectDetectionOutputElement(score=0.9486683011054993, label='person', box=ObjectDetectionBoundingBox(xmin=59, ymin=39, xmax=420, ymax=510)), ...] + ``` + """ + model_id = model or self.model + provider_helper = get_provider_helper(self.provider, task="object-detection", model=model_id) + request_parameters = provider_helper.prepare_request( + inputs=image, + parameters={"threshold": threshold}, + headers=self.headers, + model=model_id, + api_key=self.token, + ) + response = self._inner_post(request_parameters) + return ObjectDetectionOutputElement.parse_obj_as_list(response) + + def question_answering( + self, + question: str, + context: str, + *, + model: Optional[str] = None, + align_to_words: Optional[bool] = None, + doc_stride: Optional[int] = None, + handle_impossible_answer: Optional[bool] = None, + max_answer_len: Optional[int] = None, + max_question_len: Optional[int] = None, + max_seq_len: Optional[int] = None, + top_k: Optional[int] = None, + ) -> Union[QuestionAnsweringOutputElement, List[QuestionAnsweringOutputElement]]: + """ + Retrieve the answer to a question from a given text. + + Args: + question (`str`): + Question to be answered. + context (`str`): + The context of the question. + model (`str`): + The model to use for the question answering task. Can be a model ID hosted on the Hugging Face Hub or a URL to + a deployed Inference Endpoint. + align_to_words (`bool`, *optional*): + Attempts to align the answer to real words. Improves quality on space separated languages. Might hurt + on non-space-separated languages (like Japanese or Chinese) + doc_stride (`int`, *optional*): + If the context is too long to fit with the question for the model, it will be split in several chunks + with some overlap. This argument controls the size of that overlap. + handle_impossible_answer (`bool`, *optional*): + Whether to accept impossible as an answer. + max_answer_len (`int`, *optional*): + The maximum length of predicted answers (e.g., only answers with a shorter length are considered). + max_question_len (`int`, *optional*): + The maximum length of the question after tokenization. It will be truncated if needed. + max_seq_len (`int`, *optional*): + The maximum length of the total sentence (context + question) in tokens of each chunk passed to the + model. The context will be split in several chunks (using docStride as overlap) if needed. + top_k (`int`, *optional*): + The number of answers to return (will be chosen by order of likelihood). Note that we return less than + topk answers if there are not enough options available within the context. + + Returns: + Union[`QuestionAnsweringOutputElement`, List[`QuestionAnsweringOutputElement`]]: + When top_k is 1 or not provided, it returns a single `QuestionAnsweringOutputElement`. + When top_k is greater than 1, it returns a list of `QuestionAnsweringOutputElement`. + Raises: + [`InferenceTimeoutError`]: + If the model is unavailable or the request times out. + `HTTPError`: + If the request fails with an HTTP error status code other than HTTP 503. + + Example: + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient() + >>> client.question_answering(question="What's my name?", context="My name is Clara and I live in Berkeley.") + QuestionAnsweringOutputElement(answer='Clara', end=16, score=0.9326565265655518, start=11) + ``` + """ + model_id = model or self.model + provider_helper = get_provider_helper(self.provider, task="question-answering", model=model_id) + request_parameters = provider_helper.prepare_request( + inputs={"question": question, "context": context}, + parameters={ + "align_to_words": align_to_words, + "doc_stride": doc_stride, + "handle_impossible_answer": handle_impossible_answer, + "max_answer_len": max_answer_len, + "max_question_len": max_question_len, + "max_seq_len": max_seq_len, + "top_k": top_k, + }, + headers=self.headers, + model=model_id, + api_key=self.token, + ) + response = self._inner_post(request_parameters) + # Parse the response as a single `QuestionAnsweringOutputElement` when top_k is 1 or not provided, or a list of `QuestionAnsweringOutputElement` to ensure backward compatibility. + output = QuestionAnsweringOutputElement.parse_obj(response) + return output + + def sentence_similarity( + self, sentence: str, other_sentences: List[str], *, model: Optional[str] = None + ) -> List[float]: + """ + Compute the semantic similarity between a sentence and a list of other sentences by comparing their embeddings. + + Args: + sentence (`str`): + The main sentence to compare to others. + other_sentences (`List[str]`): + The list of sentences to compare to. + model (`str`, *optional*): + The model to use for the sentence similarity task. Can be a model ID hosted on the Hugging Face Hub or a URL to + a deployed Inference Endpoint. If not provided, the default recommended sentence similarity model will be used. + Defaults to None. + + Returns: + `List[float]`: The similarity scores between the main sentence and the given comparison sentences. + + Raises: + [`InferenceTimeoutError`]: + If the model is unavailable or the request times out. + `HTTPError`: + If the request fails with an HTTP error status code other than HTTP 503. + + Example: + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient() + >>> client.sentence_similarity( + ... "Machine learning is so easy.", + ... other_sentences=[ + ... "Deep learning is so straightforward.", + ... "This is so difficult, like rocket science.", + ... "I can't believe how much I struggled with this.", + ... ], + ... ) + [0.7785726189613342, 0.45876261591911316, 0.2906220555305481] + ``` + """ + model_id = model or self.model + provider_helper = get_provider_helper(self.provider, task="sentence-similarity", model=model_id) + request_parameters = provider_helper.prepare_request( + inputs={"source_sentence": sentence, "sentences": other_sentences}, + parameters={}, + extra_payload={}, + headers=self.headers, + model=model_id, + api_key=self.token, + ) + response = self._inner_post(request_parameters) + return _bytes_to_list(response) + + def summarization( + self, + text: str, + *, + model: Optional[str] = None, + clean_up_tokenization_spaces: Optional[bool] = None, + generate_parameters: Optional[Dict[str, Any]] = None, + truncation: Optional["SummarizationTruncationStrategy"] = None, + ) -> SummarizationOutput: + """ + Generate a summary of a given text using a specified model. + + Args: + text (`str`): + The input text to summarize. + model (`str`, *optional*): + The model to use for inference. Can be a model ID hosted on the Hugging Face Hub or a URL to a deployed + Inference Endpoint. If not provided, the default recommended model for summarization will be used. + clean_up_tokenization_spaces (`bool`, *optional*): + Whether to clean up the potential extra spaces in the text output. + generate_parameters (`Dict[str, Any]`, *optional*): + Additional parametrization of the text generation algorithm. + truncation (`"SummarizationTruncationStrategy"`, *optional*): + The truncation strategy to use. + Returns: + [`SummarizationOutput`]: The generated summary text. + + Raises: + [`InferenceTimeoutError`]: + If the model is unavailable or the request times out. + `HTTPError`: + If the request fails with an HTTP error status code other than HTTP 503. + + Example: + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient() + >>> client.summarization("The Eiffel tower...") + SummarizationOutput(generated_text="The Eiffel tower is one of the most famous landmarks in the world....") + ``` + """ + parameters = { + "clean_up_tokenization_spaces": clean_up_tokenization_spaces, + "generate_parameters": generate_parameters, + "truncation": truncation, + } + model_id = model or self.model + provider_helper = get_provider_helper(self.provider, task="summarization", model=model_id) + request_parameters = provider_helper.prepare_request( + inputs=text, + parameters=parameters, + headers=self.headers, + model=model_id, + api_key=self.token, + ) + response = self._inner_post(request_parameters) + return SummarizationOutput.parse_obj_as_list(response)[0] + + def table_question_answering( + self, + table: Dict[str, Any], + query: str, + *, + model: Optional[str] = None, + padding: Optional["Padding"] = None, + sequential: Optional[bool] = None, + truncation: Optional[bool] = None, + ) -> TableQuestionAnsweringOutputElement: + """ + Retrieve the answer to a question from information given in a table. + + Args: + table (`str`): + A table of data represented as a dict of lists where entries are headers and the lists are all the + values, all lists must have the same size. + query (`str`): + The query in plain text that you want to ask the table. + model (`str`): + The model to use for the table-question-answering task. Can be a model ID hosted on the Hugging Face + Hub or a URL to a deployed Inference Endpoint. + padding (`"Padding"`, *optional*): + Activates and controls padding. + sequential (`bool`, *optional*): + Whether to do inference sequentially or as a batch. Batching is faster, but models like SQA require the + inference to be done sequentially to extract relations within sequences, given their conversational + nature. + truncation (`bool`, *optional*): + Activates and controls truncation. + + Returns: + [`TableQuestionAnsweringOutputElement`]: a table question answering output containing the answer, coordinates, cells and the aggregator used. + + Raises: + [`InferenceTimeoutError`]: + If the model is unavailable or the request times out. + `HTTPError`: + If the request fails with an HTTP error status code other than HTTP 503. + + Example: + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient() + >>> query = "How many stars does the transformers repository have?" + >>> table = {"Repository": ["Transformers", "Datasets", "Tokenizers"], "Stars": ["36542", "4512", "3934"]} + >>> client.table_question_answering(table, query, model="google/tapas-base-finetuned-wtq") + TableQuestionAnsweringOutputElement(answer='36542', coordinates=[[0, 1]], cells=['36542'], aggregator='AVERAGE') + ``` + """ + model_id = model or self.model + provider_helper = get_provider_helper(self.provider, task="table-question-answering", model=model_id) + request_parameters = provider_helper.prepare_request( + inputs={"query": query, "table": table}, + parameters={"model": model, "padding": padding, "sequential": sequential, "truncation": truncation}, + headers=self.headers, + model=model_id, + api_key=self.token, + ) + response = self._inner_post(request_parameters) + return TableQuestionAnsweringOutputElement.parse_obj_as_instance(response) + + def tabular_classification(self, table: Dict[str, Any], *, model: Optional[str] = None) -> List[str]: + """ + Classifying a target category (a group) based on a set of attributes. + + Args: + table (`Dict[str, Any]`): + Set of attributes to classify. + model (`str`, *optional*): + The model to use for the tabular classification task. Can be a model ID hosted on the Hugging Face Hub or a URL to + a deployed Inference Endpoint. If not provided, the default recommended tabular classification model will be used. + Defaults to None. + + Returns: + `List`: a list of labels, one per row in the initial table. + + Raises: + [`InferenceTimeoutError`]: + If the model is unavailable or the request times out. + `HTTPError`: + If the request fails with an HTTP error status code other than HTTP 503. + + Example: + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient() + >>> table = { + ... "fixed_acidity": ["7.4", "7.8", "10.3"], + ... "volatile_acidity": ["0.7", "0.88", "0.32"], + ... "citric_acid": ["0", "0", "0.45"], + ... "residual_sugar": ["1.9", "2.6", "6.4"], + ... "chlorides": ["0.076", "0.098", "0.073"], + ... "free_sulfur_dioxide": ["11", "25", "5"], + ... "total_sulfur_dioxide": ["34", "67", "13"], + ... "density": ["0.9978", "0.9968", "0.9976"], + ... "pH": ["3.51", "3.2", "3.23"], + ... "sulphates": ["0.56", "0.68", "0.82"], + ... "alcohol": ["9.4", "9.8", "12.6"], + ... } + >>> client.tabular_classification(table=table, model="julien-c/wine-quality") + ["5", "5", "5"] + ``` + """ + model_id = model or self.model + provider_helper = get_provider_helper(self.provider, task="tabular-classification", model=model_id) + request_parameters = provider_helper.prepare_request( + inputs=None, + extra_payload={"table": table}, + parameters={}, + headers=self.headers, + model=model_id, + api_key=self.token, + ) + response = self._inner_post(request_parameters) + return _bytes_to_list(response) + + def tabular_regression(self, table: Dict[str, Any], *, model: Optional[str] = None) -> List[float]: + """ + Predicting a numerical target value given a set of attributes/features in a table. + + Args: + table (`Dict[str, Any]`): + Set of attributes stored in a table. The attributes used to predict the target can be both numerical and categorical. + model (`str`, *optional*): + The model to use for the tabular regression task. Can be a model ID hosted on the Hugging Face Hub or a URL to + a deployed Inference Endpoint. If not provided, the default recommended tabular regression model will be used. + Defaults to None. + + Returns: + `List`: a list of predicted numerical target values. + + Raises: + [`InferenceTimeoutError`]: + If the model is unavailable or the request times out. + `HTTPError`: + If the request fails with an HTTP error status code other than HTTP 503. + + Example: + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient() + >>> table = { + ... "Height": ["11.52", "12.48", "12.3778"], + ... "Length1": ["23.2", "24", "23.9"], + ... "Length2": ["25.4", "26.3", "26.5"], + ... "Length3": ["30", "31.2", "31.1"], + ... "Species": ["Bream", "Bream", "Bream"], + ... "Width": ["4.02", "4.3056", "4.6961"], + ... } + >>> client.tabular_regression(table, model="scikit-learn/Fish-Weight") + [110, 120, 130] + ``` + """ + model_id = model or self.model + provider_helper = get_provider_helper(self.provider, task="tabular-regression", model=model_id) + request_parameters = provider_helper.prepare_request( + inputs=None, + parameters={}, + extra_payload={"table": table}, + headers=self.headers, + model=model_id, + api_key=self.token, + ) + response = self._inner_post(request_parameters) + return _bytes_to_list(response) + + def text_classification( + self, + text: str, + *, + model: Optional[str] = None, + top_k: Optional[int] = None, + function_to_apply: Optional["TextClassificationOutputTransform"] = None, + ) -> List[TextClassificationOutputElement]: + """ + Perform text classification (e.g. sentiment-analysis) on the given text. + + Args: + text (`str`): + A string to be classified. + model (`str`, *optional*): + The model to use for the text classification task. Can be a model ID hosted on the Hugging Face Hub or a URL to + a deployed Inference Endpoint. If not provided, the default recommended text classification model will be used. + Defaults to None. + top_k (`int`, *optional*): + When specified, limits the output to the top K most probable classes. + function_to_apply (`"TextClassificationOutputTransform"`, *optional*): + The function to apply to the model outputs in order to retrieve the scores. + + Returns: + `List[TextClassificationOutputElement]`: a list of [`TextClassificationOutputElement`] items containing the predicted label and associated probability. + + Raises: + [`InferenceTimeoutError`]: + If the model is unavailable or the request times out. + `HTTPError`: + If the request fails with an HTTP error status code other than HTTP 503. + + Example: + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient() + >>> client.text_classification("I like you") + [ + TextClassificationOutputElement(label='POSITIVE', score=0.9998695850372314), + TextClassificationOutputElement(label='NEGATIVE', score=0.0001304351753788069), + ] + ``` + """ + model_id = model or self.model + provider_helper = get_provider_helper(self.provider, task="text-classification", model=model_id) + request_parameters = provider_helper.prepare_request( + inputs=text, + parameters={ + "function_to_apply": function_to_apply, + "top_k": top_k, + }, + headers=self.headers, + model=model_id, + api_key=self.token, + ) + response = self._inner_post(request_parameters) + return TextClassificationOutputElement.parse_obj_as_list(response)[0] # type: ignore [return-value] + + @overload + def text_generation( + self, + prompt: str, + *, + details: Literal[True], + stream: Literal[True], + model: Optional[str] = None, + # Parameters from `TextGenerationInputGenerateParameters` (maintained manually) + adapter_id: Optional[str] = None, + best_of: Optional[int] = None, + decoder_input_details: Optional[bool] = None, + do_sample: Optional[bool] = None, + frequency_penalty: Optional[float] = None, + grammar: Optional[TextGenerationInputGrammarType] = None, + max_new_tokens: Optional[int] = None, + repetition_penalty: Optional[float] = None, + return_full_text: Optional[bool] = None, + seed: Optional[int] = None, + stop: Optional[List[str]] = None, + stop_sequences: Optional[List[str]] = None, # Deprecated, use `stop` instead + temperature: Optional[float] = None, + top_k: Optional[int] = None, + top_n_tokens: Optional[int] = None, + top_p: Optional[float] = None, + truncate: Optional[int] = None, + typical_p: Optional[float] = None, + watermark: Optional[bool] = None, + ) -> Iterable[TextGenerationStreamOutput]: ... + + @overload + def text_generation( + self, + prompt: str, + *, + details: Literal[True], + stream: Optional[Literal[False]] = None, + model: Optional[str] = None, + # Parameters from `TextGenerationInputGenerateParameters` (maintained manually) + adapter_id: Optional[str] = None, + best_of: Optional[int] = None, + decoder_input_details: Optional[bool] = None, + do_sample: Optional[bool] = None, + frequency_penalty: Optional[float] = None, + grammar: Optional[TextGenerationInputGrammarType] = None, + max_new_tokens: Optional[int] = None, + repetition_penalty: Optional[float] = None, + return_full_text: Optional[bool] = None, + seed: Optional[int] = None, + stop: Optional[List[str]] = None, + stop_sequences: Optional[List[str]] = None, # Deprecated, use `stop` instead + temperature: Optional[float] = None, + top_k: Optional[int] = None, + top_n_tokens: Optional[int] = None, + top_p: Optional[float] = None, + truncate: Optional[int] = None, + typical_p: Optional[float] = None, + watermark: Optional[bool] = None, + ) -> TextGenerationOutput: ... + + @overload + def text_generation( + self, + prompt: str, + *, + details: Optional[Literal[False]] = None, + stream: Literal[True], + model: Optional[str] = None, + # Parameters from `TextGenerationInputGenerateParameters` (maintained manually) + adapter_id: Optional[str] = None, + best_of: Optional[int] = None, + decoder_input_details: Optional[bool] = None, + do_sample: Optional[bool] = None, + frequency_penalty: Optional[float] = None, + grammar: Optional[TextGenerationInputGrammarType] = None, + max_new_tokens: Optional[int] = None, + repetition_penalty: Optional[float] = None, + return_full_text: Optional[bool] = None, # Manual default value + seed: Optional[int] = None, + stop: Optional[List[str]] = None, + stop_sequences: Optional[List[str]] = None, # Deprecated, use `stop` instead + temperature: Optional[float] = None, + top_k: Optional[int] = None, + top_n_tokens: Optional[int] = None, + top_p: Optional[float] = None, + truncate: Optional[int] = None, + typical_p: Optional[float] = None, + watermark: Optional[bool] = None, + ) -> Iterable[str]: ... + + @overload + def text_generation( + self, + prompt: str, + *, + details: Optional[Literal[False]] = None, + stream: Optional[Literal[False]] = None, + model: Optional[str] = None, + # Parameters from `TextGenerationInputGenerateParameters` (maintained manually) + adapter_id: Optional[str] = None, + best_of: Optional[int] = None, + decoder_input_details: Optional[bool] = None, + do_sample: Optional[bool] = None, + frequency_penalty: Optional[float] = None, + grammar: Optional[TextGenerationInputGrammarType] = None, + max_new_tokens: Optional[int] = None, + repetition_penalty: Optional[float] = None, + return_full_text: Optional[bool] = None, + seed: Optional[int] = None, + stop: Optional[List[str]] = None, + stop_sequences: Optional[List[str]] = None, # Deprecated, use `stop` instead + temperature: Optional[float] = None, + top_k: Optional[int] = None, + top_n_tokens: Optional[int] = None, + top_p: Optional[float] = None, + truncate: Optional[int] = None, + typical_p: Optional[float] = None, + watermark: Optional[bool] = None, + ) -> str: ... + + @overload + def text_generation( + self, + prompt: str, + *, + details: Optional[bool] = None, + stream: Optional[bool] = None, + model: Optional[str] = None, + # Parameters from `TextGenerationInputGenerateParameters` (maintained manually) + adapter_id: Optional[str] = None, + best_of: Optional[int] = None, + decoder_input_details: Optional[bool] = None, + do_sample: Optional[bool] = None, + frequency_penalty: Optional[float] = None, + grammar: Optional[TextGenerationInputGrammarType] = None, + max_new_tokens: Optional[int] = None, + repetition_penalty: Optional[float] = None, + return_full_text: Optional[bool] = None, + seed: Optional[int] = None, + stop: Optional[List[str]] = None, + stop_sequences: Optional[List[str]] = None, # Deprecated, use `stop` instead + temperature: Optional[float] = None, + top_k: Optional[int] = None, + top_n_tokens: Optional[int] = None, + top_p: Optional[float] = None, + truncate: Optional[int] = None, + typical_p: Optional[float] = None, + watermark: Optional[bool] = None, + ) -> Union[str, TextGenerationOutput, Iterable[str], Iterable[TextGenerationStreamOutput]]: ... + + def text_generation( + self, + prompt: str, + *, + details: Optional[bool] = None, + stream: Optional[bool] = None, + model: Optional[str] = None, + # Parameters from `TextGenerationInputGenerateParameters` (maintained manually) + adapter_id: Optional[str] = None, + best_of: Optional[int] = None, + decoder_input_details: Optional[bool] = None, + do_sample: Optional[bool] = None, + frequency_penalty: Optional[float] = None, + grammar: Optional[TextGenerationInputGrammarType] = None, + max_new_tokens: Optional[int] = None, + repetition_penalty: Optional[float] = None, + return_full_text: Optional[bool] = None, + seed: Optional[int] = None, + stop: Optional[List[str]] = None, + stop_sequences: Optional[List[str]] = None, # Deprecated, use `stop` instead + temperature: Optional[float] = None, + top_k: Optional[int] = None, + top_n_tokens: Optional[int] = None, + top_p: Optional[float] = None, + truncate: Optional[int] = None, + typical_p: Optional[float] = None, + watermark: Optional[bool] = None, + ) -> Union[str, TextGenerationOutput, Iterable[str], Iterable[TextGenerationStreamOutput]]: + """ + Given a prompt, generate the following text. + + > [!TIP] + > If you want to generate a response from chat messages, you should use the [`InferenceClient.chat_completion`] method. + > It accepts a list of messages instead of a single text prompt and handles the chat templating for you. + + Args: + prompt (`str`): + Input text. + details (`bool`, *optional*): + By default, text_generation returns a string. Pass `details=True` if you want a detailed output (tokens, + probabilities, seed, finish reason, etc.). Only available for models running on with the + `text-generation-inference` backend. + stream (`bool`, *optional*): + By default, text_generation returns the full generated text. Pass `stream=True` if you want a stream of + tokens to be returned. Only available for models running on with the `text-generation-inference` + backend. + model (`str`, *optional*): + The model to use for inference. Can be a model ID hosted on the Hugging Face Hub or a URL to a deployed + Inference Endpoint. This parameter overrides the model defined at the instance level. Defaults to None. + adapter_id (`str`, *optional*): + Lora adapter id. + best_of (`int`, *optional*): + Generate best_of sequences and return the one if the highest token logprobs. + decoder_input_details (`bool`, *optional*): + Return the decoder input token logprobs and ids. You must set `details=True` as well for it to be taken + into account. Defaults to `False`. + do_sample (`bool`, *optional*): + Activate logits sampling + frequency_penalty (`float`, *optional*): + Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in + the text so far, decreasing the model's likelihood to repeat the same line verbatim. + grammar ([`TextGenerationInputGrammarType`], *optional*): + Grammar constraints. Can be either a JSONSchema or a regex. + max_new_tokens (`int`, *optional*): + Maximum number of generated tokens. Defaults to 100. + repetition_penalty (`float`, *optional*): + The parameter for repetition penalty. 1.0 means no penalty. See [this + paper](https://arxiv.org/pdf/1909.05858.pdf) for more details. + return_full_text (`bool`, *optional*): + Whether to prepend the prompt to the generated text + seed (`int`, *optional*): + Random sampling seed + stop (`List[str]`, *optional*): + Stop generating tokens if a member of `stop` is generated. + stop_sequences (`List[str]`, *optional*): + Deprecated argument. Use `stop` instead. + temperature (`float`, *optional*): + The value used to module the logits distribution. + top_n_tokens (`int`, *optional*): + Return information about the `top_n_tokens` most likely tokens at each generation step, instead of + just the sampled token. + top_k (`int`, *optional`): + The number of highest probability vocabulary tokens to keep for top-k-filtering. + top_p (`float`, *optional`): + If set to < 1, only the smallest set of most probable tokens with probabilities that add up to `top_p` or + higher are kept for generation. + truncate (`int`, *optional`): + Truncate inputs tokens to the given size. + typical_p (`float`, *optional`): + Typical Decoding mass + See [Typical Decoding for Natural Language Generation](https://arxiv.org/abs/2202.00666) for more information + watermark (`bool`, *optional*): + Watermarking with [A Watermark for Large Language Models](https://arxiv.org/abs/2301.10226) + + Returns: + `Union[str, TextGenerationOutput, Iterable[str], Iterable[TextGenerationStreamOutput]]`: + Generated text returned from the server: + - if `stream=False` and `details=False`, the generated text is returned as a `str` (default) + - if `stream=True` and `details=False`, the generated text is returned token by token as a `Iterable[str]` + - if `stream=False` and `details=True`, the generated text is returned with more details as a [`~huggingface_hub.TextGenerationOutput`] + - if `details=True` and `stream=True`, the generated text is returned token by token as a iterable of [`~huggingface_hub.TextGenerationStreamOutput`] + + Raises: + `ValidationError`: + If input values are not valid. No HTTP call is made to the server. + [`InferenceTimeoutError`]: + If the model is unavailable or the request times out. + `HTTPError`: + If the request fails with an HTTP error status code other than HTTP 503. + + Example: + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient() + + # Case 1: generate text + >>> client.text_generation("The huggingface_hub library is ", max_new_tokens=12) + '100% open source and built to be easy to use.' + + # Case 2: iterate over the generated tokens. Useful for large generation. + >>> for token in client.text_generation("The huggingface_hub library is ", max_new_tokens=12, stream=True): + ... print(token) + 100 + % + open + source + and + built + to + be + easy + to + use + . + + # Case 3: get more details about the generation process. + >>> client.text_generation("The huggingface_hub library is ", max_new_tokens=12, details=True) + TextGenerationOutput( + generated_text='100% open source and built to be easy to use.', + details=TextGenerationDetails( + finish_reason='length', + generated_tokens=12, + seed=None, + prefill=[ + TextGenerationPrefillOutputToken(id=487, text='The', logprob=None), + TextGenerationPrefillOutputToken(id=53789, text=' hugging', logprob=-13.171875), + (...) + TextGenerationPrefillOutputToken(id=204, text=' ', logprob=-7.0390625) + ], + tokens=[ + TokenElement(id=1425, text='100', logprob=-1.0175781, special=False), + TokenElement(id=16, text='%', logprob=-0.0463562, special=False), + (...) + TokenElement(id=25, text='.', logprob=-0.5703125, special=False) + ], + best_of_sequences=None + ) + ) + + # Case 4: iterate over the generated tokens with more details. + # Last object is more complete, containing the full generated text and the finish reason. + >>> for details in client.text_generation("The huggingface_hub library is ", max_new_tokens=12, details=True, stream=True): + ... print(details) + ... + TextGenerationStreamOutput(token=TokenElement(id=1425, text='100', logprob=-1.0175781, special=False), generated_text=None, details=None) + TextGenerationStreamOutput(token=TokenElement(id=16, text='%', logprob=-0.0463562, special=False), generated_text=None, details=None) + TextGenerationStreamOutput(token=TokenElement(id=1314, text=' open', logprob=-1.3359375, special=False), generated_text=None, details=None) + TextGenerationStreamOutput(token=TokenElement(id=3178, text=' source', logprob=-0.28100586, special=False), generated_text=None, details=None) + TextGenerationStreamOutput(token=TokenElement(id=273, text=' and', logprob=-0.5961914, special=False), generated_text=None, details=None) + TextGenerationStreamOutput(token=TokenElement(id=3426, text=' built', logprob=-1.9423828, special=False), generated_text=None, details=None) + TextGenerationStreamOutput(token=TokenElement(id=271, text=' to', logprob=-1.4121094, special=False), generated_text=None, details=None) + TextGenerationStreamOutput(token=TokenElement(id=314, text=' be', logprob=-1.5224609, special=False), generated_text=None, details=None) + TextGenerationStreamOutput(token=TokenElement(id=1833, text=' easy', logprob=-2.1132812, special=False), generated_text=None, details=None) + TextGenerationStreamOutput(token=TokenElement(id=271, text=' to', logprob=-0.08520508, special=False), generated_text=None, details=None) + TextGenerationStreamOutput(token=TokenElement(id=745, text=' use', logprob=-0.39453125, special=False), generated_text=None, details=None) + TextGenerationStreamOutput(token=TokenElement( + id=25, + text='.', + logprob=-0.5703125, + special=False), + generated_text='100% open source and built to be easy to use.', + details=TextGenerationStreamOutputStreamDetails(finish_reason='length', generated_tokens=12, seed=None) + ) + + # Case 5: generate constrained output using grammar + >>> response = client.text_generation( + ... prompt="I saw a puppy a cat and a raccoon during my bike ride in the park", + ... model="HuggingFaceH4/zephyr-orpo-141b-A35b-v0.1", + ... max_new_tokens=100, + ... repetition_penalty=1.3, + ... grammar={ + ... "type": "json", + ... "value": { + ... "properties": { + ... "location": {"type": "string"}, + ... "activity": {"type": "string"}, + ... "animals_seen": {"type": "integer", "minimum": 1, "maximum": 5}, + ... "animals": {"type": "array", "items": {"type": "string"}}, + ... }, + ... "required": ["location", "activity", "animals_seen", "animals"], + ... }, + ... }, + ... ) + >>> json.loads(response) + { + "activity": "bike riding", + "animals": ["puppy", "cat", "raccoon"], + "animals_seen": 3, + "location": "park" + } + ``` + """ + if decoder_input_details and not details: + warnings.warn( + "`decoder_input_details=True` has been passed to the server but `details=False` is set meaning that" + " the output from the server will be truncated." + ) + decoder_input_details = False + + if stop_sequences is not None: + warnings.warn( + "`stop_sequences` is a deprecated argument for `text_generation` task" + " and will be removed in version '0.28.0'. Use `stop` instead.", + FutureWarning, + ) + if stop is None: + stop = stop_sequences # use deprecated arg if provided + + # Build payload + parameters = { + "adapter_id": adapter_id, + "best_of": best_of, + "decoder_input_details": decoder_input_details, + "details": details, + "do_sample": do_sample, + "frequency_penalty": frequency_penalty, + "grammar": grammar, + "max_new_tokens": max_new_tokens, + "repetition_penalty": repetition_penalty, + "return_full_text": return_full_text, + "seed": seed, + "stop": stop, + "temperature": temperature, + "top_k": top_k, + "top_n_tokens": top_n_tokens, + "top_p": top_p, + "truncate": truncate, + "typical_p": typical_p, + "watermark": watermark, + } + + # Remove some parameters if not a TGI server + unsupported_kwargs = _get_unsupported_text_generation_kwargs(model) + if len(unsupported_kwargs) > 0: + # The server does not support some parameters + # => means it is not a TGI server + # => remove unsupported parameters and warn the user + + ignored_parameters = [] + for key in unsupported_kwargs: + if parameters.get(key): + ignored_parameters.append(key) + parameters.pop(key, None) + if len(ignored_parameters) > 0: + warnings.warn( + "API endpoint/model for text-generation is not served via TGI. Ignoring following parameters:" + f" {', '.join(ignored_parameters)}.", + UserWarning, + ) + if details: + warnings.warn( + "API endpoint/model for text-generation is not served via TGI. Parameter `details=True` will" + " be ignored meaning only the generated text will be returned.", + UserWarning, + ) + details = False + if stream: + raise ValueError( + "API endpoint/model for text-generation is not served via TGI. Cannot return output as a stream." + " Please pass `stream=False` as input." + ) + + model_id = model or self.model + provider_helper = get_provider_helper(self.provider, task="text-generation", model=model_id) + request_parameters = provider_helper.prepare_request( + inputs=prompt, + parameters=parameters, + extra_payload={"stream": stream}, + headers=self.headers, + model=model_id, + api_key=self.token, + ) + + # Handle errors separately for more precise error messages + try: + bytes_output = self._inner_post(request_parameters, stream=stream or False) + except HTTPError as e: + match = MODEL_KWARGS_NOT_USED_REGEX.search(str(e)) + if isinstance(e, BadRequestError) and match: + unused_params = [kwarg.strip("' ") for kwarg in match.group(1).split(",")] + _set_unsupported_text_generation_kwargs(model, unused_params) + return self.text_generation( # type: ignore + prompt=prompt, + details=details, + stream=stream, + model=model_id, + adapter_id=adapter_id, + best_of=best_of, + decoder_input_details=decoder_input_details, + do_sample=do_sample, + frequency_penalty=frequency_penalty, + grammar=grammar, + max_new_tokens=max_new_tokens, + repetition_penalty=repetition_penalty, + return_full_text=return_full_text, + seed=seed, + stop=stop, + temperature=temperature, + top_k=top_k, + top_n_tokens=top_n_tokens, + top_p=top_p, + truncate=truncate, + typical_p=typical_p, + watermark=watermark, + ) + raise_text_generation_error(e) + + # Parse output + if stream: + return _stream_text_generation_response(bytes_output, details) # type: ignore + + data = _bytes_to_dict(bytes_output) # type: ignore[arg-type] + + # Data can be a single element (dict) or an iterable of dicts where we select the first element of. + if isinstance(data, list): + data = data[0] + response = provider_helper.get_response(data, request_parameters) + return TextGenerationOutput.parse_obj_as_instance(response) if details else response["generated_text"] + + def text_to_image( + self, + prompt: str, + *, + negative_prompt: Optional[str] = None, + height: Optional[int] = None, + width: Optional[int] = None, + num_inference_steps: Optional[int] = None, + guidance_scale: Optional[float] = None, + model: Optional[str] = None, + scheduler: Optional[str] = None, + seed: Optional[int] = None, + extra_body: Optional[Dict[str, Any]] = None, + ) -> "Image": + """ + Generate an image based on a given text using a specified model. + + > [!WARNING] + > You must have `PIL` installed if you want to work with images (`pip install Pillow`). + + > [!TIP] + > You can pass provider-specific parameters to the model by using the `extra_body` argument. + + Args: + prompt (`str`): + The prompt to generate an image from. + negative_prompt (`str`, *optional*): + One prompt to guide what NOT to include in image generation. + height (`int`, *optional*): + The height in pixels of the output image + width (`int`, *optional*): + The width in pixels of the output image + num_inference_steps (`int`, *optional*): + The number of denoising steps. More denoising steps usually lead to a higher quality image at the + expense of slower inference. + guidance_scale (`float`, *optional*): + A higher guidance scale value encourages the model to generate images closely linked to the text + prompt, but values too high may cause saturation and other artifacts. + model (`str`, *optional*): + The model to use for inference. Can be a model ID hosted on the Hugging Face Hub or a URL to a deployed + Inference Endpoint. If not provided, the default recommended text-to-image model will be used. + Defaults to None. + scheduler (`str`, *optional*): + Override the scheduler with a compatible one. + seed (`int`, *optional*): + Seed for the random number generator. + extra_body (`Dict[str, Any]`, *optional*): + Additional provider-specific parameters to pass to the model. Refer to the provider's documentation + for supported parameters. + + Returns: + `Image`: The generated image. + + Raises: + [`InferenceTimeoutError`]: + If the model is unavailable or the request times out. + `HTTPError`: + If the request fails with an HTTP error status code other than HTTP 503. + + Example: + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient() + + >>> image = client.text_to_image("An astronaut riding a horse on the moon.") + >>> image.save("astronaut.png") + + >>> image = client.text_to_image( + ... "An astronaut riding a horse on the moon.", + ... negative_prompt="low resolution, blurry", + ... model="stabilityai/stable-diffusion-2-1", + ... ) + >>> image.save("better_astronaut.png") + ``` + Example using a third-party provider directly. Usage will be billed on your fal.ai account. + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient( + ... provider="fal-ai", # Use fal.ai provider + ... api_key="fal-ai-api-key", # Pass your fal.ai API key + ... ) + >>> image = client.text_to_image( + ... "A majestic lion in a fantasy forest", + ... model="black-forest-labs/FLUX.1-schnell", + ... ) + >>> image.save("lion.png") + ``` + + Example using a third-party provider through Hugging Face Routing. Usage will be billed on your Hugging Face account. + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient( + ... provider="replicate", # Use replicate provider + ... api_key="hf_...", # Pass your HF token + ... ) + >>> image = client.text_to_image( + ... "An astronaut riding a horse on the moon.", + ... model="black-forest-labs/FLUX.1-dev", + ... ) + >>> image.save("astronaut.png") + ``` + + Example using Replicate provider with extra parameters + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient( + ... provider="replicate", # Use replicate provider + ... api_key="hf_...", # Pass your HF token + ... ) + >>> image = client.text_to_image( + ... "An astronaut riding a horse on the moon.", + ... model="black-forest-labs/FLUX.1-schnell", + ... extra_body={"output_quality": 100}, + ... ) + >>> image.save("astronaut.png") + ``` + """ + model_id = model or self.model + provider_helper = get_provider_helper(self.provider, task="text-to-image", model=model_id) + request_parameters = provider_helper.prepare_request( + inputs=prompt, + parameters={ + "negative_prompt": negative_prompt, + "height": height, + "width": width, + "num_inference_steps": num_inference_steps, + "guidance_scale": guidance_scale, + "scheduler": scheduler, + "seed": seed, + **(extra_body or {}), + }, + headers=self.headers, + model=model_id, + api_key=self.token, + ) + response = self._inner_post(request_parameters) + response = provider_helper.get_response(response) + return _bytes_to_image(response) + + def text_to_video( + self, + prompt: str, + *, + model: Optional[str] = None, + guidance_scale: Optional[float] = None, + negative_prompt: Optional[List[str]] = None, + num_frames: Optional[float] = None, + num_inference_steps: Optional[int] = None, + seed: Optional[int] = None, + extra_body: Optional[Dict[str, Any]] = None, + ) -> bytes: + """ + Generate a video based on a given text. + + > [!TIP] + > You can pass provider-specific parameters to the model by using the `extra_body` argument. + + Args: + prompt (`str`): + The prompt to generate a video from. + model (`str`, *optional*): + The model to use for inference. Can be a model ID hosted on the Hugging Face Hub or a URL to a deployed + Inference Endpoint. If not provided, the default recommended text-to-video model will be used. + Defaults to None. + guidance_scale (`float`, *optional*): + A higher guidance scale value encourages the model to generate videos closely linked to the text + prompt, but values too high may cause saturation and other artifacts. + negative_prompt (`List[str]`, *optional*): + One or several prompt to guide what NOT to include in video generation. + num_frames (`float`, *optional*): + The num_frames parameter determines how many video frames are generated. + num_inference_steps (`int`, *optional*): + The number of denoising steps. More denoising steps usually lead to a higher quality video at the + expense of slower inference. + seed (`int`, *optional*): + Seed for the random number generator. + extra_body (`Dict[str, Any]`, *optional*): + Additional provider-specific parameters to pass to the model. Refer to the provider's documentation + for supported parameters. + + Returns: + `bytes`: The generated video. + + Example: + + Example using a third-party provider directly. Usage will be billed on your fal.ai account. + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient( + ... provider="fal-ai", # Using fal.ai provider + ... api_key="fal-ai-api-key", # Pass your fal.ai API key + ... ) + >>> video = client.text_to_video( + ... "A majestic lion running in a fantasy forest", + ... model="tencent/HunyuanVideo", + ... ) + >>> with open("lion.mp4", "wb") as file: + ... file.write(video) + ``` + + Example using a third-party provider through Hugging Face Routing. Usage will be billed on your Hugging Face account. + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient( + ... provider="replicate", # Using replicate provider + ... api_key="hf_...", # Pass your HF token + ... ) + >>> video = client.text_to_video( + ... "A cat running in a park", + ... model="genmo/mochi-1-preview", + ... ) + >>> with open("cat.mp4", "wb") as file: + ... file.write(video) + ``` + """ + model_id = model or self.model + provider_helper = get_provider_helper(self.provider, task="text-to-video", model=model_id) + request_parameters = provider_helper.prepare_request( + inputs=prompt, + parameters={ + "guidance_scale": guidance_scale, + "negative_prompt": negative_prompt, + "num_frames": num_frames, + "num_inference_steps": num_inference_steps, + "seed": seed, + **(extra_body or {}), + }, + headers=self.headers, + model=model_id, + api_key=self.token, + ) + response = self._inner_post(request_parameters) + response = provider_helper.get_response(response, request_parameters) + return response + + def text_to_speech( + self, + text: str, + *, + model: Optional[str] = None, + do_sample: Optional[bool] = None, + early_stopping: Optional[Union[bool, "TextToSpeechEarlyStoppingEnum"]] = None, + epsilon_cutoff: Optional[float] = None, + eta_cutoff: Optional[float] = None, + max_length: Optional[int] = None, + max_new_tokens: Optional[int] = None, + min_length: Optional[int] = None, + min_new_tokens: Optional[int] = None, + num_beam_groups: Optional[int] = None, + num_beams: Optional[int] = None, + penalty_alpha: Optional[float] = None, + temperature: Optional[float] = None, + top_k: Optional[int] = None, + top_p: Optional[float] = None, + typical_p: Optional[float] = None, + use_cache: Optional[bool] = None, + extra_body: Optional[Dict[str, Any]] = None, + ) -> bytes: + """ + Synthesize an audio of a voice pronouncing a given text. + + > [!TIP] + > You can pass provider-specific parameters to the model by using the `extra_body` argument. + + Args: + text (`str`): + The text to synthesize. + model (`str`, *optional*): + The model to use for inference. Can be a model ID hosted on the Hugging Face Hub or a URL to a deployed + Inference Endpoint. If not provided, the default recommended text-to-speech model will be used. + Defaults to None. + do_sample (`bool`, *optional*): + Whether to use sampling instead of greedy decoding when generating new tokens. + early_stopping (`Union[bool, "TextToSpeechEarlyStoppingEnum"]`, *optional*): + Controls the stopping condition for beam-based methods. + epsilon_cutoff (`float`, *optional*): + If set to float strictly between 0 and 1, only tokens with a conditional probability greater than + epsilon_cutoff will be sampled. In the paper, suggested values range from 3e-4 to 9e-4, depending on + the size of the model. See [Truncation Sampling as Language Model + Desmoothing](https://hf.co/papers/2210.15191) for more details. + eta_cutoff (`float`, *optional*): + Eta sampling is a hybrid of locally typical sampling and epsilon sampling. If set to float strictly + between 0 and 1, a token is only considered if it is greater than either eta_cutoff or sqrt(eta_cutoff) + * exp(-entropy(softmax(next_token_logits))). The latter term is intuitively the expected next token + probability, scaled by sqrt(eta_cutoff). In the paper, suggested values range from 3e-4 to 2e-3, + depending on the size of the model. See [Truncation Sampling as Language Model + Desmoothing](https://hf.co/papers/2210.15191) for more details. + max_length (`int`, *optional*): + The maximum length (in tokens) of the generated text, including the input. + max_new_tokens (`int`, *optional*): + The maximum number of tokens to generate. Takes precedence over max_length. + min_length (`int`, *optional*): + The minimum length (in tokens) of the generated text, including the input. + min_new_tokens (`int`, *optional*): + The minimum number of tokens to generate. Takes precedence over min_length. + num_beam_groups (`int`, *optional*): + Number of groups to divide num_beams into in order to ensure diversity among different groups of beams. + See [this paper](https://hf.co/papers/1610.02424) for more details. + num_beams (`int`, *optional*): + Number of beams to use for beam search. + penalty_alpha (`float`, *optional*): + The value balances the model confidence and the degeneration penalty in contrastive search decoding. + temperature (`float`, *optional*): + The value used to modulate the next token probabilities. + top_k (`int`, *optional*): + The number of highest probability vocabulary tokens to keep for top-k-filtering. + top_p (`float`, *optional*): + If set to float < 1, only the smallest set of most probable tokens with probabilities that add up to + top_p or higher are kept for generation. + typical_p (`float`, *optional*): + Local typicality measures how similar the conditional probability of predicting a target token next is + to the expected conditional probability of predicting a random token next, given the partial text + already generated. If set to float < 1, the smallest set of the most locally typical tokens with + probabilities that add up to typical_p or higher are kept for generation. See [this + paper](https://hf.co/papers/2202.00666) for more details. + use_cache (`bool`, *optional*): + Whether the model should use the past last key/values attentions to speed up decoding + extra_body (`Dict[str, Any]`, *optional*): + Additional provider-specific parameters to pass to the model. Refer to the provider's documentation + for supported parameters. + Returns: + `bytes`: The generated audio. + + Raises: + [`InferenceTimeoutError`]: + If the model is unavailable or the request times out. + `HTTPError`: + If the request fails with an HTTP error status code other than HTTP 503. + + Example: + ```py + >>> from pathlib import Path + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient() + + >>> audio = client.text_to_speech("Hello world") + >>> Path("hello_world.flac").write_bytes(audio) + ``` + + Example using a third-party provider directly. Usage will be billed on your Replicate account. + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient( + ... provider="replicate", + ... api_key="your-replicate-api-key", # Pass your Replicate API key directly + ... ) + >>> audio = client.text_to_speech( + ... text="Hello world", + ... model="OuteAI/OuteTTS-0.3-500M", + ... ) + >>> Path("hello_world.flac").write_bytes(audio) + ``` + + Example using a third-party provider through Hugging Face Routing. Usage will be billed on your Hugging Face account. + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient( + ... provider="replicate", + ... api_key="hf_...", # Pass your HF token + ... ) + >>> audio =client.text_to_speech( + ... text="Hello world", + ... model="OuteAI/OuteTTS-0.3-500M", + ... ) + >>> Path("hello_world.flac").write_bytes(audio) + ``` + Example using Replicate provider with extra parameters + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient( + ... provider="replicate", # Use replicate provider + ... api_key="hf_...", # Pass your HF token + ... ) + >>> audio = client.text_to_speech( + ... "Hello, my name is Kororo, an awesome text-to-speech model.", + ... model="hexgrad/Kokoro-82M", + ... extra_body={"voice": "af_nicole"}, + ... ) + >>> Path("hello.flac").write_bytes(audio) + ``` + + Example music-gen using "YuE-s1-7B-anneal-en-cot" on fal.ai + ```py + >>> from huggingface_hub import InferenceClient + >>> lyrics = ''' + ... [verse] + ... In the town where I was born + ... Lived a man who sailed to sea + ... And he told us of his life + ... In the land of submarines + ... So we sailed on to the sun + ... 'Til we found a sea of green + ... And we lived beneath the waves + ... In our yellow submarine + + ... [chorus] + ... We all live in a yellow submarine + ... Yellow submarine, yellow submarine + ... We all live in a yellow submarine + ... Yellow submarine, yellow submarine + ... ''' + >>> genres = "pavarotti-style tenor voice" + >>> client = InferenceClient( + ... provider="fal-ai", + ... model="m-a-p/YuE-s1-7B-anneal-en-cot", + ... api_key=..., + ... ) + >>> audio = client.text_to_speech(lyrics, extra_body={"genres": genres}) + >>> with open("output.mp3", "wb") as f: + ... f.write(audio) + ``` + """ + model_id = model or self.model + provider_helper = get_provider_helper(self.provider, task="text-to-speech", model=model_id) + request_parameters = provider_helper.prepare_request( + inputs=text, + parameters={ + "do_sample": do_sample, + "early_stopping": early_stopping, + "epsilon_cutoff": epsilon_cutoff, + "eta_cutoff": eta_cutoff, + "max_length": max_length, + "max_new_tokens": max_new_tokens, + "min_length": min_length, + "min_new_tokens": min_new_tokens, + "num_beam_groups": num_beam_groups, + "num_beams": num_beams, + "penalty_alpha": penalty_alpha, + "temperature": temperature, + "top_k": top_k, + "top_p": top_p, + "typical_p": typical_p, + "use_cache": use_cache, + **(extra_body or {}), + }, + headers=self.headers, + model=model_id, + api_key=self.token, + ) + response = self._inner_post(request_parameters) + response = provider_helper.get_response(response) + return response + + def token_classification( + self, + text: str, + *, + model: Optional[str] = None, + aggregation_strategy: Optional["TokenClassificationAggregationStrategy"] = None, + ignore_labels: Optional[List[str]] = None, + stride: Optional[int] = None, + ) -> List[TokenClassificationOutputElement]: + """ + Perform token classification on the given text. + Usually used for sentence parsing, either grammatical, or Named Entity Recognition (NER) to understand keywords contained within text. + + Args: + text (`str`): + A string to be classified. + model (`str`, *optional*): + The model to use for the token classification task. Can be a model ID hosted on the Hugging Face Hub or a URL to + a deployed Inference Endpoint. If not provided, the default recommended token classification model will be used. + Defaults to None. + aggregation_strategy (`"TokenClassificationAggregationStrategy"`, *optional*): + The strategy used to fuse tokens based on model predictions + ignore_labels (`List[str`, *optional*): + A list of labels to ignore + stride (`int`, *optional*): + The number of overlapping tokens between chunks when splitting the input text. + + Returns: + `List[TokenClassificationOutputElement]`: List of [`TokenClassificationOutputElement`] items containing the entity group, confidence score, word, start and end index. + + Raises: + [`InferenceTimeoutError`]: + If the model is unavailable or the request times out. + `HTTPError`: + If the request fails with an HTTP error status code other than HTTP 503. + + Example: + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient() + >>> client.token_classification("My name is Sarah Jessica Parker but you can call me Jessica") + [ + TokenClassificationOutputElement( + entity_group='PER', + score=0.9971321225166321, + word='Sarah Jessica Parker', + start=11, + end=31, + ), + TokenClassificationOutputElement( + entity_group='PER', + score=0.9773476123809814, + word='Jessica', + start=52, + end=59, + ) + ] + ``` + """ + model_id = model or self.model + provider_helper = get_provider_helper(self.provider, task="token-classification", model=model_id) + request_parameters = provider_helper.prepare_request( + inputs=text, + parameters={ + "aggregation_strategy": aggregation_strategy, + "ignore_labels": ignore_labels, + "stride": stride, + }, + headers=self.headers, + model=model_id, + api_key=self.token, + ) + response = self._inner_post(request_parameters) + return TokenClassificationOutputElement.parse_obj_as_list(response) + + def translation( + self, + text: str, + *, + model: Optional[str] = None, + src_lang: Optional[str] = None, + tgt_lang: Optional[str] = None, + clean_up_tokenization_spaces: Optional[bool] = None, + truncation: Optional["TranslationTruncationStrategy"] = None, + generate_parameters: Optional[Dict[str, Any]] = None, + ) -> TranslationOutput: + """ + Convert text from one language to another. + + Check out https://huggingface.co/tasks/translation for more information on how to choose the best model for + your specific use case. Source and target languages usually depend on the model. + However, it is possible to specify source and target languages for certain models. If you are working with one of these models, + you can use `src_lang` and `tgt_lang` arguments to pass the relevant information. + + Args: + text (`str`): + A string to be translated. + model (`str`, *optional*): + The model to use for the translation task. Can be a model ID hosted on the Hugging Face Hub or a URL to + a deployed Inference Endpoint. If not provided, the default recommended translation model will be used. + Defaults to None. + src_lang (`str`, *optional*): + The source language of the text. Required for models that can translate from multiple languages. + tgt_lang (`str`, *optional*): + Target language to translate to. Required for models that can translate to multiple languages. + clean_up_tokenization_spaces (`bool`, *optional*): + Whether to clean up the potential extra spaces in the text output. + truncation (`"TranslationTruncationStrategy"`, *optional*): + The truncation strategy to use. + generate_parameters (`Dict[str, Any]`, *optional*): + Additional parametrization of the text generation algorithm. + + Returns: + [`TranslationOutput`]: The generated translated text. + + Raises: + [`InferenceTimeoutError`]: + If the model is unavailable or the request times out. + `HTTPError`: + If the request fails with an HTTP error status code other than HTTP 503. + `ValueError`: + If only one of the `src_lang` and `tgt_lang` arguments are provided. + + Example: + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient() + >>> client.translation("My name is Wolfgang and I live in Berlin") + 'Mein Name ist Wolfgang und ich lebe in Berlin.' + >>> client.translation("My name is Wolfgang and I live in Berlin", model="Helsinki-NLP/opus-mt-en-fr") + TranslationOutput(translation_text='Je m'appelle Wolfgang et je vis à Berlin.') + ``` + + Specifying languages: + ```py + >>> client.translation("My name is Sarah Jessica Parker but you can call me Jessica", model="facebook/mbart-large-50-many-to-many-mmt", src_lang="en_XX", tgt_lang="fr_XX") + "Mon nom est Sarah Jessica Parker mais vous pouvez m'appeler Jessica" + ``` + """ + # Throw error if only one of `src_lang` and `tgt_lang` was given + if src_lang is not None and tgt_lang is None: + raise ValueError("You cannot specify `src_lang` without specifying `tgt_lang`.") + + if src_lang is None and tgt_lang is not None: + raise ValueError("You cannot specify `tgt_lang` without specifying `src_lang`.") + + model_id = model or self.model + provider_helper = get_provider_helper(self.provider, task="translation", model=model_id) + request_parameters = provider_helper.prepare_request( + inputs=text, + parameters={ + "src_lang": src_lang, + "tgt_lang": tgt_lang, + "clean_up_tokenization_spaces": clean_up_tokenization_spaces, + "truncation": truncation, + "generate_parameters": generate_parameters, + }, + headers=self.headers, + model=model_id, + api_key=self.token, + ) + response = self._inner_post(request_parameters) + return TranslationOutput.parse_obj_as_list(response)[0] + + def visual_question_answering( + self, + image: ContentT, + question: str, + *, + model: Optional[str] = None, + top_k: Optional[int] = None, + ) -> List[VisualQuestionAnsweringOutputElement]: + """ + Answering open-ended questions based on an image. + + Args: + image (`Union[str, Path, bytes, BinaryIO, PIL.Image.Image]`): + The input image for the context. It can be raw bytes, an image file, a URL to an online image, or a PIL Image. + question (`str`): + Question to be answered. + model (`str`, *optional*): + The model to use for the visual question answering task. Can be a model ID hosted on the Hugging Face Hub or a URL to + a deployed Inference Endpoint. If not provided, the default recommended visual question answering model will be used. + Defaults to None. + top_k (`int`, *optional*): + The number of answers to return (will be chosen by order of likelihood). Note that we return less than + topk answers if there are not enough options available within the context. + Returns: + `List[VisualQuestionAnsweringOutputElement]`: a list of [`VisualQuestionAnsweringOutputElement`] items containing the predicted label and associated probability. + + Raises: + `InferenceTimeoutError`: + If the model is unavailable or the request times out. + `HTTPError`: + If the request fails with an HTTP error status code other than HTTP 503. + + Example: + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient() + >>> client.visual_question_answering( + ... image="https://huggingface.co/datasets/mishig/sample_images/resolve/main/tiger.jpg", + ... question="What is the animal doing?" + ... ) + [ + VisualQuestionAnsweringOutputElement(score=0.778609573841095, answer='laying down'), + VisualQuestionAnsweringOutputElement(score=0.6957435607910156, answer='sitting'), + ] + ``` + """ + model_id = model or self.model + provider_helper = get_provider_helper(self.provider, task="visual-question-answering", model=model_id) + request_parameters = provider_helper.prepare_request( + inputs=image, + parameters={"top_k": top_k}, + headers=self.headers, + model=model_id, + api_key=self.token, + extra_payload={"question": question, "image": _b64_encode(image)}, + ) + response = self._inner_post(request_parameters) + return VisualQuestionAnsweringOutputElement.parse_obj_as_list(response) + + def zero_shot_classification( + self, + text: str, + candidate_labels: List[str], + *, + multi_label: Optional[bool] = False, + hypothesis_template: Optional[str] = None, + model: Optional[str] = None, + ) -> List[ZeroShotClassificationOutputElement]: + """ + Provide as input a text and a set of candidate labels to classify the input text. + + Args: + text (`str`): + The input text to classify. + candidate_labels (`List[str]`): + The set of possible class labels to classify the text into. + labels (`List[str]`, *optional*): + (deprecated) List of strings. Each string is the verbalization of a possible label for the input text. + multi_label (`bool`, *optional*): + Whether multiple candidate labels can be true. If false, the scores are normalized such that the sum of + the label likelihoods for each sequence is 1. If true, the labels are considered independent and + probabilities are normalized for each candidate. + hypothesis_template (`str`, *optional*): + The sentence used in conjunction with `candidate_labels` to attempt the text classification by + replacing the placeholder with the candidate labels. + model (`str`, *optional*): + The model to use for inference. Can be a model ID hosted on the Hugging Face Hub or a URL to a deployed + Inference Endpoint. This parameter overrides the model defined at the instance level. If not provided, the default recommended zero-shot classification model will be used. + + + Returns: + `List[ZeroShotClassificationOutputElement]`: List of [`ZeroShotClassificationOutputElement`] items containing the predicted labels and their confidence. + + Raises: + [`InferenceTimeoutError`]: + If the model is unavailable or the request times out. + `HTTPError`: + If the request fails with an HTTP error status code other than HTTP 503. + + Example with `multi_label=False`: + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient() + >>> text = ( + ... "A new model offers an explanation for how the Galilean satellites formed around the solar system's" + ... "largest world. Konstantin Batygin did not set out to solve one of the solar system's most puzzling" + ... " mysteries when he went for a run up a hill in Nice, France." + ... ) + >>> labels = ["space & cosmos", "scientific discovery", "microbiology", "robots", "archeology"] + >>> client.zero_shot_classification(text, labels) + [ + ZeroShotClassificationOutputElement(label='scientific discovery', score=0.7961668968200684), + ZeroShotClassificationOutputElement(label='space & cosmos', score=0.18570658564567566), + ZeroShotClassificationOutputElement(label='microbiology', score=0.00730885099619627), + ZeroShotClassificationOutputElement(label='archeology', score=0.006258360575884581), + ZeroShotClassificationOutputElement(label='robots', score=0.004559356719255447), + ] + >>> client.zero_shot_classification(text, labels, multi_label=True) + [ + ZeroShotClassificationOutputElement(label='scientific discovery', score=0.9829297661781311), + ZeroShotClassificationOutputElement(label='space & cosmos', score=0.755190908908844), + ZeroShotClassificationOutputElement(label='microbiology', score=0.0005462635890580714), + ZeroShotClassificationOutputElement(label='archeology', score=0.00047131875180639327), + ZeroShotClassificationOutputElement(label='robots', score=0.00030448526376858354), + ] + ``` + + Example with `multi_label=True` and a custom `hypothesis_template`: + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient() + >>> client.zero_shot_classification( + ... text="I really like our dinner and I'm very happy. I don't like the weather though.", + ... labels=["positive", "negative", "pessimistic", "optimistic"], + ... multi_label=True, + ... hypothesis_template="This text is {} towards the weather" + ... ) + [ + ZeroShotClassificationOutputElement(label='negative', score=0.9231801629066467), + ZeroShotClassificationOutputElement(label='pessimistic', score=0.8760990500450134), + ZeroShotClassificationOutputElement(label='optimistic', score=0.0008674879791215062), + ZeroShotClassificationOutputElement(label='positive', score=0.0005250611575320363) + ] + ``` + """ + model_id = model or self.model + provider_helper = get_provider_helper(self.provider, task="zero-shot-classification", model=model_id) + request_parameters = provider_helper.prepare_request( + inputs=text, + parameters={ + "candidate_labels": candidate_labels, + "multi_label": multi_label, + "hypothesis_template": hypothesis_template, + }, + headers=self.headers, + model=model_id, + api_key=self.token, + ) + response = self._inner_post(request_parameters) + output = _bytes_to_dict(response) + return [ + ZeroShotClassificationOutputElement.parse_obj_as_instance({"label": label, "score": score}) + for label, score in zip(output["labels"], output["scores"]) + ] + + def zero_shot_image_classification( + self, + image: ContentT, + candidate_labels: List[str], + *, + model: Optional[str] = None, + hypothesis_template: Optional[str] = None, + # deprecated argument + labels: List[str] = None, # type: ignore + ) -> List[ZeroShotImageClassificationOutputElement]: + """ + Provide input image and text labels to predict text labels for the image. + + Args: + image (`Union[str, Path, bytes, BinaryIO, PIL.Image.Image]`): + The input image to caption. It can be raw bytes, an image file, a URL to an online image, or a PIL Image. + candidate_labels (`List[str]`): + The candidate labels for this image + labels (`List[str]`, *optional*): + (deprecated) List of string possible labels. There must be at least 2 labels. + model (`str`, *optional*): + The model to use for inference. Can be a model ID hosted on the Hugging Face Hub or a URL to a deployed + Inference Endpoint. This parameter overrides the model defined at the instance level. If not provided, the default recommended zero-shot image classification model will be used. + hypothesis_template (`str`, *optional*): + The sentence used in conjunction with `candidate_labels` to attempt the image classification by + replacing the placeholder with the candidate labels. + + Returns: + `List[ZeroShotImageClassificationOutputElement]`: List of [`ZeroShotImageClassificationOutputElement`] items containing the predicted labels and their confidence. + + Raises: + [`InferenceTimeoutError`]: + If the model is unavailable or the request times out. + `HTTPError`: + If the request fails with an HTTP error status code other than HTTP 503. + + Example: + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient() + + >>> client.zero_shot_image_classification( + ... "https://upload.wikimedia.org/wikipedia/commons/thumb/4/43/Cute_dog.jpg/320px-Cute_dog.jpg", + ... labels=["dog", "cat", "horse"], + ... ) + [ZeroShotImageClassificationOutputElement(label='dog', score=0.956),...] + ``` + """ + # Raise ValueError if input is less than 2 labels + if len(candidate_labels) < 2: + raise ValueError("You must specify at least 2 classes to compare.") + + model_id = model or self.model + provider_helper = get_provider_helper(self.provider, task="zero-shot-image-classification", model=model_id) + request_parameters = provider_helper.prepare_request( + inputs=image, + parameters={ + "candidate_labels": candidate_labels, + "hypothesis_template": hypothesis_template, + }, + headers=self.headers, + model=model_id, + api_key=self.token, + ) + response = self._inner_post(request_parameters) + return ZeroShotImageClassificationOutputElement.parse_obj_as_list(response) + + def get_endpoint_info(self, *, model: Optional[str] = None) -> Dict[str, Any]: + """ + Get information about the deployed endpoint. + + This endpoint is only available on endpoints powered by Text-Generation-Inference (TGI) or Text-Embedding-Inference (TEI). + Endpoints powered by `transformers` return an empty payload. + + Args: + model (`str`, *optional*): + The model to use for inference. Can be a model ID hosted on the Hugging Face Hub or a URL to a deployed + Inference Endpoint. This parameter overrides the model defined at the instance level. Defaults to None. + + Returns: + `Dict[str, Any]`: Information about the endpoint. + + Example: + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient("meta-llama/Meta-Llama-3-70B-Instruct") + >>> client.get_endpoint_info() + { + 'model_id': 'meta-llama/Meta-Llama-3-70B-Instruct', + 'model_sha': None, + 'model_dtype': 'torch.float16', + 'model_device_type': 'cuda', + 'model_pipeline_tag': None, + 'max_concurrent_requests': 128, + 'max_best_of': 2, + 'max_stop_sequences': 4, + 'max_input_length': 8191, + 'max_total_tokens': 8192, + 'waiting_served_ratio': 0.3, + 'max_batch_total_tokens': 1259392, + 'max_waiting_tokens': 20, + 'max_batch_size': None, + 'validation_workers': 32, + 'max_client_batch_size': 4, + 'version': '2.0.2', + 'sha': 'dccab72549635c7eb5ddb17f43f0b7cdff07c214', + 'docker_label': 'sha-dccab72' + } + ``` + """ + if self.provider != "hf-inference": + raise ValueError(f"Getting endpoint info is not supported on '{self.provider}'.") + + model = model or self.model + if model is None: + raise ValueError("Model id not provided.") + if model.startswith(("http://", "https://")): + url = model.rstrip("/") + "/info" + else: + url = f"{constants.INFERENCE_ENDPOINT}/models/{model}/info" + + response = get_session().get(url, headers=build_hf_headers(token=self.token)) + hf_raise_for_status(response) + return response.json() + + def health_check(self, model: Optional[str] = None) -> bool: + """ + Check the health of the deployed endpoint. + + Health check is only available with Inference Endpoints powered by Text-Generation-Inference (TGI) or Text-Embedding-Inference (TEI). + + Args: + model (`str`, *optional*): + URL of the Inference Endpoint. This parameter overrides the model defined at the instance level. Defaults to None. + + Returns: + `bool`: True if everything is working fine. + + Example: + ```py + >>> from huggingface_hub import InferenceClient + >>> client = InferenceClient("https://jzgu0buei5.us-east-1.aws.endpoints.huggingface.cloud") + >>> client.health_check() + True + ``` + """ + if self.provider != "hf-inference": + raise ValueError(f"Health check is not supported on '{self.provider}'.") + + model = model or self.model + if model is None: + raise ValueError("Model id not provided.") + if not model.startswith(("http://", "https://")): + raise ValueError("Model must be an Inference Endpoint URL.") + url = model.rstrip("/") + "/health" + + response = get_session().get(url, headers=build_hf_headers(token=self.token)) + return response.status_code == 200 + + @property + def chat(self) -> "ProxyClientChat": + return ProxyClientChat(self) + + +class _ProxyClient: + """Proxy class to be able to call `client.chat.completion.create(...)` as OpenAI client.""" + + def __init__(self, client: InferenceClient): + self._client = client + + +class ProxyClientChat(_ProxyClient): + """Proxy class to be able to call `client.chat.completion.create(...)` as OpenAI client.""" + + @property + def completions(self) -> "ProxyClientChatCompletions": + return ProxyClientChatCompletions(self._client) + + +class ProxyClientChatCompletions(_ProxyClient): + """Proxy class to be able to call `client.chat.completion.create(...)` as OpenAI client.""" + + @property + def create(self): + return self._client.chat_completion diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/inference/_common.py b/venv/lib/python3.13/site-packages/huggingface_hub/inference/_common.py new file mode 100644 index 0000000000000000000000000000000000000000..c7803d14eee9161739f25f9fb5914a35469be0ff --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/inference/_common.py @@ -0,0 +1,459 @@ +# coding=utf-8 +# Copyright 2023-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains utilities used by both the sync and async inference clients.""" + +import base64 +import io +import json +import logging +import mimetypes +from dataclasses import dataclass +from pathlib import Path +from typing import ( + TYPE_CHECKING, + Any, + AsyncIterable, + BinaryIO, + Dict, + Iterable, + List, + Literal, + NoReturn, + Optional, + Union, + overload, +) + +from requests import HTTPError + +from huggingface_hub.errors import ( + GenerationError, + IncompleteGenerationError, + OverloadedError, + TextGenerationError, + UnknownError, + ValidationError, +) + +from ..utils import get_session, is_aiohttp_available, is_numpy_available, is_pillow_available +from ._generated.types import ChatCompletionStreamOutput, TextGenerationStreamOutput + + +if TYPE_CHECKING: + from aiohttp import ClientResponse, ClientSession + from PIL.Image import Image + +# TYPES +UrlT = str +PathT = Union[str, Path] +ContentT = Union[bytes, BinaryIO, PathT, UrlT, "Image", bytearray, memoryview] + +# Use to set a Accept: image/png header +TASKS_EXPECTING_IMAGES = {"text-to-image", "image-to-image"} + +logger = logging.getLogger(__name__) + + +@dataclass +class RequestParameters: + url: str + task: str + model: Optional[str] + json: Optional[Union[str, Dict, List]] + data: Optional[bytes] + headers: Dict[str, Any] + + +class MimeBytes(bytes): + """ + A bytes object with a mime type. + To be returned by `_prepare_payload_open_as_mime_bytes` in subclasses. + + Example: + ```python + >>> b = MimeBytes(b"hello", "text/plain") + >>> isinstance(b, bytes) + True + >>> b.mime_type + 'text/plain' + ``` + """ + + mime_type: Optional[str] + + def __new__(cls, data: bytes, mime_type: Optional[str] = None): + obj = super().__new__(cls, data) + obj.mime_type = mime_type + if isinstance(data, MimeBytes) and mime_type is None: + obj.mime_type = data.mime_type + return obj + + +## IMPORT UTILS + + +def _import_aiohttp(): + # Make sure `aiohttp` is installed on the machine. + if not is_aiohttp_available(): + raise ImportError("Please install aiohttp to use `AsyncInferenceClient` (`pip install aiohttp`).") + import aiohttp + + return aiohttp + + +def _import_numpy(): + """Make sure `numpy` is installed on the machine.""" + if not is_numpy_available(): + raise ImportError("Please install numpy to use deal with embeddings (`pip install numpy`).") + import numpy + + return numpy + + +def _import_pil_image(): + """Make sure `PIL` is installed on the machine.""" + if not is_pillow_available(): + raise ImportError( + "Please install Pillow to use deal with images (`pip install Pillow`). If you don't want the image to be" + " post-processed, use `client.post(...)` and get the raw response from the server." + ) + from PIL import Image + + return Image + + +## ENCODING / DECODING UTILS + + +@overload +def _open_as_mime_bytes(content: ContentT) -> MimeBytes: ... # means "if input is not None, output is not None" + + +@overload +def _open_as_mime_bytes(content: Literal[None]) -> Literal[None]: ... # means "if input is None, output is None" + + +def _open_as_mime_bytes(content: Optional[ContentT]) -> Optional[MimeBytes]: + """Open `content` as a binary file, either from a URL, a local path, raw bytes, or a PIL Image. + + Do nothing if `content` is None. + """ + # If content is None, yield None + if content is None: + return None + + # If content is bytes, return it + if isinstance(content, bytes): + return MimeBytes(content) + + # If content is raw binary data (bytearray, memoryview) + if isinstance(content, (bytearray, memoryview)): + return MimeBytes(bytes(content)) + + # If content is a binary file-like object + if hasattr(content, "read"): # duck-typing instead of isinstance(content, BinaryIO) + logger.debug("Reading content from BinaryIO") + data = content.read() + mime_type = mimetypes.guess_type(content.name)[0] if hasattr(content, "name") else None + if isinstance(data, str): + raise TypeError("Expected binary stream (bytes), but got text stream") + return MimeBytes(data, mime_type=mime_type) + + # If content is a string => must be either a URL or a path + if isinstance(content, str): + if content.startswith("https://") or content.startswith("http://"): + logger.debug(f"Downloading content from {content}") + response = get_session().get(content) + mime_type = response.headers.get("Content-Type") + if mime_type is None: + mime_type = mimetypes.guess_type(content)[0] + return MimeBytes(response.content, mime_type=mime_type) + + content = Path(content) + if not content.exists(): + raise FileNotFoundError( + f"File not found at {content}. If `data` is a string, it must either be a URL or a path to a local" + " file. To pass raw content, please encode it as bytes first." + ) + + # If content is a Path => open it + if isinstance(content, Path): + logger.debug(f"Opening content from {content}") + return MimeBytes(content.read_bytes(), mime_type=mimetypes.guess_type(content)[0]) + + # If content is a PIL Image => convert to bytes + if is_pillow_available(): + from PIL import Image + + if isinstance(content, Image.Image): + logger.debug("Converting PIL Image to bytes") + buffer = io.BytesIO() + format = content.format or "PNG" + content.save(buffer, format=format) + return MimeBytes(buffer.getvalue(), mime_type=f"image/{format.lower()}") + + # If nothing matched, raise error + raise TypeError( + f"Unsupported content type: {type(content)}. " + "Expected one of: bytes, bytearray, BinaryIO, memoryview, Path, str (URL or file path), or PIL.Image.Image." + ) + + +def _b64_encode(content: ContentT) -> str: + """Encode a raw file (image, audio) into base64. Can be bytes, an opened file, a path or a URL.""" + raw_bytes = _open_as_mime_bytes(content) + return base64.b64encode(raw_bytes).decode() + + +def _as_url(content: ContentT, default_mime_type: str) -> str: + if isinstance(content, str) and content.startswith(("http://", "https://", "data:")): + return content + + # Convert content to bytes + raw_bytes = _open_as_mime_bytes(content) + + # Get MIME type + mime_type = raw_bytes.mime_type or default_mime_type + + # Encode content to base64 + encoded_data = base64.b64encode(raw_bytes).decode() + + # Build data URL + return f"data:{mime_type};base64,{encoded_data}" + + +def _b64_to_image(encoded_image: str) -> "Image": + """Parse a base64-encoded string into a PIL Image.""" + Image = _import_pil_image() + return Image.open(io.BytesIO(base64.b64decode(encoded_image))) + + +def _bytes_to_list(content: bytes) -> List: + """Parse bytes from a Response object into a Python list. + + Expects the response body to be JSON-encoded data. + + NOTE: This is exactly the same implementation as `_bytes_to_dict` and will not complain if the returned data is a + dictionary. The only advantage of having both is to help the user (and mypy) understand what kind of data to expect. + """ + return json.loads(content.decode()) + + +def _bytes_to_dict(content: bytes) -> Dict: + """Parse bytes from a Response object into a Python dictionary. + + Expects the response body to be JSON-encoded data. + + NOTE: This is exactly the same implementation as `_bytes_to_list` and will not complain if the returned data is a + list. The only advantage of having both is to help the user (and mypy) understand what kind of data to expect. + """ + return json.loads(content.decode()) + + +def _bytes_to_image(content: bytes) -> "Image": + """Parse bytes from a Response object into a PIL Image. + + Expects the response body to be raw bytes. To deal with b64 encoded images, use `_b64_to_image` instead. + """ + Image = _import_pil_image() + return Image.open(io.BytesIO(content)) + + +def _as_dict(response: Union[bytes, Dict]) -> Dict: + return json.loads(response) if isinstance(response, bytes) else response + + +## STREAMING UTILS + + +def _stream_text_generation_response( + bytes_output_as_lines: Iterable[bytes], details: bool +) -> Union[Iterable[str], Iterable[TextGenerationStreamOutput]]: + """Used in `InferenceClient.text_generation`.""" + # Parse ServerSentEvents + for byte_payload in bytes_output_as_lines: + try: + output = _format_text_generation_stream_output(byte_payload, details) + except StopIteration: + break + if output is not None: + yield output + + +async def _async_stream_text_generation_response( + bytes_output_as_lines: AsyncIterable[bytes], details: bool +) -> Union[AsyncIterable[str], AsyncIterable[TextGenerationStreamOutput]]: + """Used in `AsyncInferenceClient.text_generation`.""" + # Parse ServerSentEvents + async for byte_payload in bytes_output_as_lines: + try: + output = _format_text_generation_stream_output(byte_payload, details) + except StopIteration: + break + if output is not None: + yield output + + +def _format_text_generation_stream_output( + byte_payload: bytes, details: bool +) -> Optional[Union[str, TextGenerationStreamOutput]]: + if not byte_payload.startswith(b"data:"): + return None # empty line + + if byte_payload.strip() == b"data: [DONE]": + raise StopIteration("[DONE] signal received.") + + # Decode payload + payload = byte_payload.decode("utf-8") + json_payload = json.loads(payload.lstrip("data:").rstrip("/n")) + + # Either an error as being returned + if json_payload.get("error") is not None: + raise _parse_text_generation_error(json_payload["error"], json_payload.get("error_type")) + + # Or parse token payload + output = TextGenerationStreamOutput.parse_obj_as_instance(json_payload) + return output.token.text if not details else output + + +def _stream_chat_completion_response( + bytes_lines: Iterable[bytes], +) -> Iterable[ChatCompletionStreamOutput]: + """Used in `InferenceClient.chat_completion` if model is served with TGI.""" + for item in bytes_lines: + try: + output = _format_chat_completion_stream_output(item) + except StopIteration: + break + if output is not None: + yield output + + +async def _async_stream_chat_completion_response( + bytes_lines: AsyncIterable[bytes], +) -> AsyncIterable[ChatCompletionStreamOutput]: + """Used in `AsyncInferenceClient.chat_completion`.""" + async for item in bytes_lines: + try: + output = _format_chat_completion_stream_output(item) + except StopIteration: + break + if output is not None: + yield output + + +def _format_chat_completion_stream_output( + byte_payload: bytes, +) -> Optional[ChatCompletionStreamOutput]: + if not byte_payload.startswith(b"data:"): + return None # empty line + + if byte_payload.strip() == b"data: [DONE]": + raise StopIteration("[DONE] signal received.") + + # Decode payload + payload = byte_payload.decode("utf-8") + json_payload = json.loads(payload.lstrip("data:").rstrip("/n")) + + # Either an error as being returned + if json_payload.get("error") is not None: + raise _parse_text_generation_error(json_payload["error"], json_payload.get("error_type")) + + # Or parse token payload + return ChatCompletionStreamOutput.parse_obj_as_instance(json_payload) + + +async def _async_yield_from(client: "ClientSession", response: "ClientResponse") -> AsyncIterable[bytes]: + try: + async for byte_payload in response.content: + yield byte_payload.strip() + finally: + # Always close the underlying HTTP session to avoid resource leaks + await client.close() + + +# "TGI servers" are servers running with the `text-generation-inference` backend. +# This backend is the go-to solution to run large language models at scale. However, +# for some smaller models (e.g. "gpt2") the default `transformers` + `api-inference` +# solution is still in use. +# +# Both approaches have very similar APIs, but not exactly the same. What we do first in +# the `text_generation` method is to assume the model is served via TGI. If we realize +# it's not the case (i.e. we receive an HTTP 400 Bad Request), we fallback to the +# default API with a warning message. When that's the case, We remember the unsupported +# attributes for this model in the `_UNSUPPORTED_TEXT_GENERATION_KWARGS` global variable. +# +# In addition, TGI servers have a built-in API route for chat-completion, which is not +# available on the default API. We use this route to provide a more consistent behavior +# when available. +# +# For more details, see https://github.com/huggingface/text-generation-inference and +# https://huggingface.co/docs/api-inference/detailed_parameters#text-generation-task. + +_UNSUPPORTED_TEXT_GENERATION_KWARGS: Dict[Optional[str], List[str]] = {} + + +def _set_unsupported_text_generation_kwargs(model: Optional[str], unsupported_kwargs: List[str]) -> None: + _UNSUPPORTED_TEXT_GENERATION_KWARGS.setdefault(model, []).extend(unsupported_kwargs) + + +def _get_unsupported_text_generation_kwargs(model: Optional[str]) -> List[str]: + return _UNSUPPORTED_TEXT_GENERATION_KWARGS.get(model, []) + + +# TEXT GENERATION ERRORS +# ---------------------- +# Text-generation errors are parsed separately to handle as much as possible the errors returned by the text generation +# inference project (https://github.com/huggingface/text-generation-inference). +# ---------------------- + + +def raise_text_generation_error(http_error: HTTPError) -> NoReturn: + """ + Try to parse text-generation-inference error message and raise HTTPError in any case. + + Args: + error (`HTTPError`): + The HTTPError that have been raised. + """ + # Try to parse a Text Generation Inference error + + try: + # Hacky way to retrieve payload in case of aiohttp error + payload = getattr(http_error, "response_error_payload", None) or http_error.response.json() + error = payload.get("error") + error_type = payload.get("error_type") + except Exception: # no payload + raise http_error + + # If error_type => more information than `hf_raise_for_status` + if error_type is not None: + exception = _parse_text_generation_error(error, error_type) + raise exception from http_error + + # Otherwise, fallback to default error + raise http_error + + +def _parse_text_generation_error(error: Optional[str], error_type: Optional[str]) -> TextGenerationError: + if error_type == "generation": + return GenerationError(error) # type: ignore + if error_type == "incomplete_generation": + return IncompleteGenerationError(error) # type: ignore + if error_type == "overloaded": + return OverloadedError(error) # type: ignore + if error_type == "validation": + return ValidationError(error) # type: ignore + return UnknownError(error) # type: ignore diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/serialization/__init__.py b/venv/lib/python3.13/site-packages/huggingface_hub/serialization/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..8949a22a5f65ab29b7df65aa6a9df9bce0544b7e --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/serialization/__init__.py @@ -0,0 +1,27 @@ +# Copyright 2024 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ruff: noqa: F401 +"""Contains helpers to serialize tensors.""" + +from ._base import StateDictSplit, split_state_dict_into_shards_factory +from ._tensorflow import get_tf_storage_size, split_tf_state_dict_into_shards +from ._torch import ( + get_torch_storage_id, + get_torch_storage_size, + load_state_dict_from_file, + load_torch_model, + save_torch_model, + save_torch_state_dict, + split_torch_state_dict_into_shards, +) diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/serialization/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/serialization/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..70c701ea5172e79a14312a2e84bc77612ddd33cd Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/serialization/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/serialization/__pycache__/_base.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/serialization/__pycache__/_base.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9ecfa4dcda50cd9e9903a07f4e83e8ea8f112b84 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/serialization/__pycache__/_base.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/serialization/__pycache__/_tensorflow.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/serialization/__pycache__/_tensorflow.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b8c4d9b6f4feffa97b749fc18091b3b23b28cd90 Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/serialization/__pycache__/_tensorflow.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/serialization/__pycache__/_torch.cpython-313.pyc b/venv/lib/python3.13/site-packages/huggingface_hub/serialization/__pycache__/_torch.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..36ac7aebf787d3cbff9b1b4e33e0542898f0df3e Binary files /dev/null and b/venv/lib/python3.13/site-packages/huggingface_hub/serialization/__pycache__/_torch.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/serialization/_base.py b/venv/lib/python3.13/site-packages/huggingface_hub/serialization/_base.py new file mode 100644 index 0000000000000000000000000000000000000000..b79c82f5dba58d252b5c3a7345f0df09794b55ce --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/serialization/_base.py @@ -0,0 +1,207 @@ +# Copyright 2024 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains helpers to split tensors into shards.""" + +from dataclasses import dataclass, field +from typing import Any, Callable, Dict, List, Optional, TypeVar, Union + +from .. import logging + + +TensorT = TypeVar("TensorT") +TensorSizeFn_T = Callable[[TensorT], int] +StorageIDFn_T = Callable[[TensorT], Optional[Any]] + +MAX_SHARD_SIZE = "5GB" +SIZE_UNITS = { + "TB": 10**12, + "GB": 10**9, + "MB": 10**6, + "KB": 10**3, +} + + +logger = logging.get_logger(__file__) + + +@dataclass +class StateDictSplit: + is_sharded: bool = field(init=False) + metadata: Dict[str, Any] + filename_to_tensors: Dict[str, List[str]] + tensor_to_filename: Dict[str, str] + + def __post_init__(self): + self.is_sharded = len(self.filename_to_tensors) > 1 + + +def split_state_dict_into_shards_factory( + state_dict: Dict[str, TensorT], + *, + get_storage_size: TensorSizeFn_T, + filename_pattern: str, + get_storage_id: StorageIDFn_T = lambda tensor: None, + max_shard_size: Union[int, str] = MAX_SHARD_SIZE, +) -> StateDictSplit: + """ + Split a model state dictionary in shards so that each shard is smaller than a given size. + + The shards are determined by iterating through the `state_dict` in the order of its keys. There is no optimization + made to make each shard as close as possible to the maximum size passed. For example, if the limit is 10GB and we + have tensors of sizes [6GB, 6GB, 2GB, 6GB, 2GB, 2GB] they will get sharded as [6GB], [6+2GB], [6+2+2GB] and not + [6+2+2GB], [6+2GB], [6GB]. + + > [!WARNING] + > If one of the model's tensor is bigger than `max_shard_size`, it will end up in its own shard which will have a + > size greater than `max_shard_size`. + + Args: + state_dict (`Dict[str, Tensor]`): + The state dictionary to save. + get_storage_size (`Callable[[Tensor], int]`): + A function that returns the size of a tensor when saved on disk in bytes. + get_storage_id (`Callable[[Tensor], Optional[Any]]`, *optional*): + A function that returns a unique identifier to a tensor storage. Multiple different tensors can share the + same underlying storage. This identifier is guaranteed to be unique and constant for this tensor's storage + during its lifetime. Two tensor storages with non-overlapping lifetimes may have the same id. + filename_pattern (`str`, *optional*): + The pattern to generate the files names in which the model will be saved. Pattern must be a string that + can be formatted with `filename_pattern.format(suffix=...)` and must contain the keyword `suffix` + max_shard_size (`int` or `str`, *optional*): + The maximum size of each shard, in bytes. Defaults to 5GB. + + Returns: + [`StateDictSplit`]: A `StateDictSplit` object containing the shards and the index to retrieve them. + """ + storage_id_to_tensors: Dict[Any, List[str]] = {} + + shard_list: List[Dict[str, TensorT]] = [] + current_shard: Dict[str, TensorT] = {} + current_shard_size = 0 + total_size = 0 + + if isinstance(max_shard_size, str): + max_shard_size = parse_size_to_int(max_shard_size) + + for key, tensor in state_dict.items(): + # when bnb serialization is used the weights in the state dict can be strings + # check: https://github.com/huggingface/transformers/pull/24416 for more details + if isinstance(tensor, str): + logger.info("Skipping tensor %s as it is a string (bnb serialization)", key) + continue + + # If a `tensor` shares the same underlying storage as another tensor, we put `tensor` in the same `block` + storage_id = get_storage_id(tensor) + if storage_id is not None: + if storage_id in storage_id_to_tensors: + # We skip this tensor for now and will reassign to correct shard later + storage_id_to_tensors[storage_id].append(key) + continue + else: + # This is the first tensor with this storage_id, we create a new entry + # in the storage_id_to_tensors dict => we will assign the shard id later + storage_id_to_tensors[storage_id] = [key] + + # Compute tensor size + tensor_size = get_storage_size(tensor) + + # If this tensor is bigger than the maximal size, we put it in its own shard + if tensor_size > max_shard_size: + total_size += tensor_size + shard_list.append({key: tensor}) + continue + + # If this tensor is going to tip up over the maximal size, we split. + # Current shard already has some tensors, we add it to the list of shards and create a new one. + if current_shard_size + tensor_size > max_shard_size: + shard_list.append(current_shard) + current_shard = {} + current_shard_size = 0 + + # Add the tensor to the current shard + current_shard[key] = tensor + current_shard_size += tensor_size + total_size += tensor_size + + # Add the last shard + if len(current_shard) > 0: + shard_list.append(current_shard) + nb_shards = len(shard_list) + + # Loop over the tensors that share the same storage and assign them together + for storage_id, keys in storage_id_to_tensors.items(): + # Let's try to find the shard where the first tensor of this storage is and put all tensors in the same shard + for shard in shard_list: + if keys[0] in shard: + for key in keys: + shard[key] = state_dict[key] + break + + # If we only have one shard, we return it => no need to build the index + if nb_shards == 1: + filename = filename_pattern.format(suffix="") + return StateDictSplit( + metadata={"total_size": total_size}, + filename_to_tensors={filename: list(state_dict.keys())}, + tensor_to_filename={key: filename for key in state_dict.keys()}, + ) + + # Now that each tensor is assigned to a shard, let's assign a filename to each shard + tensor_name_to_filename = {} + filename_to_tensors = {} + for idx, shard in enumerate(shard_list): + filename = filename_pattern.format(suffix=f"-{idx + 1:05d}-of-{nb_shards:05d}") + for key in shard: + tensor_name_to_filename[key] = filename + filename_to_tensors[filename] = list(shard.keys()) + + # Build the index and return + return StateDictSplit( + metadata={"total_size": total_size}, + filename_to_tensors=filename_to_tensors, + tensor_to_filename=tensor_name_to_filename, + ) + + +def parse_size_to_int(size_as_str: str) -> int: + """ + Parse a size expressed as a string with digits and unit (like `"5MB"`) to an integer (in bytes). + + Supported units are "TB", "GB", "MB", "KB". + + Args: + size_as_str (`str`): The size to convert. Will be directly returned if an `int`. + + Example: + + ```py + >>> parse_size_to_int("5MB") + 5000000 + ``` + """ + size_as_str = size_as_str.strip() + + # Parse unit + unit = size_as_str[-2:].upper() + if unit not in SIZE_UNITS: + raise ValueError(f"Unit '{unit}' not supported. Supported units are TB, GB, MB, KB. Got '{size_as_str}'.") + multiplier = SIZE_UNITS[unit] + + # Parse value + try: + value = float(size_as_str[:-2].strip()) + except ValueError as e: + raise ValueError(f"Could not parse the size value from '{size_as_str}': {e}") from e + + return int(value * multiplier) diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/serialization/_dduf.py b/venv/lib/python3.13/site-packages/huggingface_hub/serialization/_dduf.py new file mode 100644 index 0000000000000000000000000000000000000000..a1debadb3ac8a45716f0359b932dc065f09edb84 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/serialization/_dduf.py @@ -0,0 +1,387 @@ +import json +import logging +import mmap +import os +import shutil +import zipfile +from contextlib import contextmanager +from dataclasses import dataclass, field +from pathlib import Path +from typing import Any, Dict, Generator, Iterable, Tuple, Union + +from ..errors import DDUFCorruptedFileError, DDUFExportError, DDUFInvalidEntryNameError + + +logger = logging.getLogger(__name__) + +DDUF_ALLOWED_ENTRIES = { + # Allowed file extensions in a DDUF file + ".json", + ".model", + ".safetensors", + ".txt", +} + +DDUF_FOLDER_REQUIRED_ENTRIES = { + # Each folder must contain at least one of these entries + "config.json", + "tokenizer_config.json", + "preprocessor_config.json", + "scheduler_config.json", +} + + +@dataclass +class DDUFEntry: + """Object representing a file entry in a DDUF file. + + See [`read_dduf_file`] for how to read a DDUF file. + + Attributes: + filename (str): + The name of the file in the DDUF archive. + offset (int): + The offset of the file in the DDUF archive. + length (int): + The length of the file in the DDUF archive. + dduf_path (str): + The path to the DDUF archive (for internal use). + """ + + filename: str + length: int + offset: int + + dduf_path: Path = field(repr=False) + + @contextmanager + def as_mmap(self) -> Generator[bytes, None, None]: + """Open the file as a memory-mapped file. + + Useful to load safetensors directly from the file. + + Example: + ```py + >>> import safetensors.torch + >>> with entry.as_mmap() as mm: + ... tensors = safetensors.torch.load(mm) + ``` + """ + with self.dduf_path.open("rb") as f: + with mmap.mmap(f.fileno(), length=0, access=mmap.ACCESS_READ) as mm: + yield mm[self.offset : self.offset + self.length] + + def read_text(self, encoding: str = "utf-8") -> str: + """Read the file as text. + + Useful for '.txt' and '.json' entries. + + Example: + ```py + >>> import json + >>> index = json.loads(entry.read_text()) + ``` + """ + with self.dduf_path.open("rb") as f: + f.seek(self.offset) + return f.read(self.length).decode(encoding=encoding) + + +def read_dduf_file(dduf_path: Union[os.PathLike, str]) -> Dict[str, DDUFEntry]: + """ + Read a DDUF file and return a dictionary of entries. + + Only the metadata is read, the data is not loaded in memory. + + Args: + dduf_path (`str` or `os.PathLike`): + The path to the DDUF file to read. + + Returns: + `Dict[str, DDUFEntry]`: + A dictionary of [`DDUFEntry`] indexed by filename. + + Raises: + - [`DDUFCorruptedFileError`]: If the DDUF file is corrupted (i.e. doesn't follow the DDUF format). + + Example: + ```python + >>> import json + >>> import safetensors.torch + >>> from huggingface_hub import read_dduf_file + + # Read DDUF metadata + >>> dduf_entries = read_dduf_file("FLUX.1-dev.dduf") + + # Returns a mapping filename <> DDUFEntry + >>> dduf_entries["model_index.json"] + DDUFEntry(filename='model_index.json', offset=66, length=587) + + # Load model index as JSON + >>> json.loads(dduf_entries["model_index.json"].read_text()) + {'_class_name': 'FluxPipeline', '_diffusers_version': '0.32.0.dev0', '_name_or_path': 'black-forest-labs/FLUX.1-dev', ... + + # Load VAE weights using safetensors + >>> with dduf_entries["vae/diffusion_pytorch_model.safetensors"].as_mmap() as mm: + ... state_dict = safetensors.torch.load(mm) + ``` + """ + entries = {} + dduf_path = Path(dduf_path) + logger.info(f"Reading DDUF file {dduf_path}") + with zipfile.ZipFile(str(dduf_path), "r") as zf: + for info in zf.infolist(): + logger.debug(f"Reading entry {info.filename}") + if info.compress_type != zipfile.ZIP_STORED: + raise DDUFCorruptedFileError("Data must not be compressed in DDUF file.") + + try: + _validate_dduf_entry_name(info.filename) + except DDUFInvalidEntryNameError as e: + raise DDUFCorruptedFileError(f"Invalid entry name in DDUF file: {info.filename}") from e + + offset = _get_data_offset(zf, info) + + entries[info.filename] = DDUFEntry( + filename=info.filename, offset=offset, length=info.file_size, dduf_path=dduf_path + ) + + # Consistency checks on the DDUF file + if "model_index.json" not in entries: + raise DDUFCorruptedFileError("Missing required 'model_index.json' entry in DDUF file.") + index = json.loads(entries["model_index.json"].read_text()) + _validate_dduf_structure(index, entries.keys()) + + logger.info(f"Done reading DDUF file {dduf_path}. Found {len(entries)} entries") + return entries + + +def export_entries_as_dduf( + dduf_path: Union[str, os.PathLike], entries: Iterable[Tuple[str, Union[str, Path, bytes]]] +) -> None: + """Write a DDUF file from an iterable of entries. + + This is a lower-level helper than [`export_folder_as_dduf`] that allows more flexibility when serializing data. + In particular, you don't need to save the data on disk before exporting it in the DDUF file. + + Args: + dduf_path (`str` or `os.PathLike`): + The path to the DDUF file to write. + entries (`Iterable[Tuple[str, Union[str, Path, bytes]]]`): + An iterable of entries to write in the DDUF file. Each entry is a tuple with the filename and the content. + The filename should be the path to the file in the DDUF archive. + The content can be a string or a pathlib.Path representing a path to a file on the local disk or directly the content as bytes. + + Raises: + - [`DDUFExportError`]: If anything goes wrong during the export (e.g. invalid entry name, missing 'model_index.json', etc.). + + Example: + ```python + # Export specific files from the local disk. + >>> from huggingface_hub import export_entries_as_dduf + >>> export_entries_as_dduf( + ... dduf_path="stable-diffusion-v1-4-FP16.dduf", + ... entries=[ # List entries to add to the DDUF file (here, only FP16 weights) + ... ("model_index.json", "path/to/model_index.json"), + ... ("vae/config.json", "path/to/vae/config.json"), + ... ("vae/diffusion_pytorch_model.fp16.safetensors", "path/to/vae/diffusion_pytorch_model.fp16.safetensors"), + ... ("text_encoder/config.json", "path/to/text_encoder/config.json"), + ... ("text_encoder/model.fp16.safetensors", "path/to/text_encoder/model.fp16.safetensors"), + ... # ... add more entries here + ... ] + ... ) + ``` + + ```python + # Export state_dicts one by one from a loaded pipeline + >>> from diffusers import DiffusionPipeline + >>> from typing import Generator, Tuple + >>> import safetensors.torch + >>> from huggingface_hub import export_entries_as_dduf + >>> pipe = DiffusionPipeline.from_pretrained("CompVis/stable-diffusion-v1-4") + ... # ... do some work with the pipeline + + >>> def as_entries(pipe: DiffusionPipeline) -> Generator[Tuple[str, bytes], None, None]: + ... # Build an generator that yields the entries to add to the DDUF file. + ... # The first element of the tuple is the filename in the DDUF archive (must use UNIX separator!). The second element is the content of the file. + ... # Entries will be evaluated lazily when the DDUF file is created (only 1 entry is loaded in memory at a time) + ... yield "vae/config.json", pipe.vae.to_json_string().encode() + ... yield "vae/diffusion_pytorch_model.safetensors", safetensors.torch.save(pipe.vae.state_dict()) + ... yield "text_encoder/config.json", pipe.text_encoder.config.to_json_string().encode() + ... yield "text_encoder/model.safetensors", safetensors.torch.save(pipe.text_encoder.state_dict()) + ... # ... add more entries here + + >>> export_entries_as_dduf(dduf_path="stable-diffusion-v1-4.dduf", entries=as_entries(pipe)) + ``` + """ + logger.info(f"Exporting DDUF file '{dduf_path}'") + filenames = set() + index = None + with zipfile.ZipFile(str(dduf_path), "w", zipfile.ZIP_STORED) as archive: + for filename, content in entries: + if filename in filenames: + raise DDUFExportError(f"Can't add duplicate entry: {filename}") + filenames.add(filename) + + if filename == "model_index.json": + try: + index = json.loads(_load_content(content).decode()) + except json.JSONDecodeError as e: + raise DDUFExportError("Failed to parse 'model_index.json'.") from e + + try: + filename = _validate_dduf_entry_name(filename) + except DDUFInvalidEntryNameError as e: + raise DDUFExportError(f"Invalid entry name: {filename}") from e + logger.debug(f"Adding entry '{filename}' to DDUF file") + _dump_content_in_archive(archive, filename, content) + + # Consistency checks on the DDUF file + if index is None: + raise DDUFExportError("Missing required 'model_index.json' entry in DDUF file.") + try: + _validate_dduf_structure(index, filenames) + except DDUFCorruptedFileError as e: + raise DDUFExportError("Invalid DDUF file structure.") from e + + logger.info(f"Done writing DDUF file {dduf_path}") + + +def export_folder_as_dduf(dduf_path: Union[str, os.PathLike], folder_path: Union[str, os.PathLike]) -> None: + """ + Export a folder as a DDUF file. + + AUses [`export_entries_as_dduf`] under the hood. + + Args: + dduf_path (`str` or `os.PathLike`): + The path to the DDUF file to write. + folder_path (`str` or `os.PathLike`): + The path to the folder containing the diffusion model. + + Example: + ```python + >>> from huggingface_hub import export_folder_as_dduf + >>> export_folder_as_dduf(dduf_path="FLUX.1-dev.dduf", folder_path="path/to/FLUX.1-dev") + ``` + """ + folder_path = Path(folder_path) + + def _iterate_over_folder() -> Iterable[Tuple[str, Path]]: + for path in Path(folder_path).glob("**/*"): + if not path.is_file(): + continue + if path.suffix not in DDUF_ALLOWED_ENTRIES: + logger.debug(f"Skipping file '{path}' (file type not allowed)") + continue + path_in_archive = path.relative_to(folder_path) + if len(path_in_archive.parts) >= 3: + logger.debug(f"Skipping file '{path}' (nested directories not allowed)") + continue + yield path_in_archive.as_posix(), path + + export_entries_as_dduf(dduf_path, _iterate_over_folder()) + + +def _dump_content_in_archive(archive: zipfile.ZipFile, filename: str, content: Union[str, os.PathLike, bytes]) -> None: + with archive.open(filename, "w", force_zip64=True) as archive_fh: + if isinstance(content, (str, Path)): + content_path = Path(content) + with content_path.open("rb") as content_fh: + shutil.copyfileobj(content_fh, archive_fh, 1024 * 1024 * 8) # type: ignore[misc] + elif isinstance(content, bytes): + archive_fh.write(content) + else: + raise DDUFExportError(f"Invalid content type for {filename}. Must be str, Path or bytes.") + + +def _load_content(content: Union[str, Path, bytes]) -> bytes: + """Load the content of an entry as bytes. + + Used only for small checks (not to dump content into archive). + """ + if isinstance(content, (str, Path)): + return Path(content).read_bytes() + elif isinstance(content, bytes): + return content + else: + raise DDUFExportError(f"Invalid content type. Must be str, Path or bytes. Got {type(content)}.") + + +def _validate_dduf_entry_name(entry_name: str) -> str: + if "." + entry_name.split(".")[-1] not in DDUF_ALLOWED_ENTRIES: + raise DDUFInvalidEntryNameError(f"File type not allowed: {entry_name}") + if "\\" in entry_name: + raise DDUFInvalidEntryNameError(f"Entry names must use UNIX separators ('/'). Got {entry_name}.") + entry_name = entry_name.strip("/") + if entry_name.count("/") > 1: + raise DDUFInvalidEntryNameError(f"DDUF only supports 1 level of directory. Got {entry_name}.") + return entry_name + + +def _validate_dduf_structure(index: Any, entry_names: Iterable[str]) -> None: + """ + Consistency checks on the DDUF file structure. + + Rules: + - The 'model_index.json' entry is required and must contain a dictionary. + - Each folder name must correspond to an entry in 'model_index.json'. + - Each folder must contain at least a config file ('config.json', 'tokenizer_config.json', 'preprocessor_config.json', 'scheduler_config.json'). + + Args: + index (Any): + The content of the 'model_index.json' entry. + entry_names (Iterable[str]): + The list of entry names in the DDUF file. + + Raises: + - [`DDUFCorruptedFileError`]: If the DDUF file is corrupted (i.e. doesn't follow the DDUF format). + """ + if not isinstance(index, dict): + raise DDUFCorruptedFileError(f"Invalid 'model_index.json' content. Must be a dictionary. Got {type(index)}.") + + dduf_folders = {entry.split("/")[0] for entry in entry_names if "/" in entry} + for folder in dduf_folders: + if folder not in index: + raise DDUFCorruptedFileError(f"Missing required entry '{folder}' in 'model_index.json'.") + if not any(f"{folder}/{required_entry}" in entry_names for required_entry in DDUF_FOLDER_REQUIRED_ENTRIES): + raise DDUFCorruptedFileError( + f"Missing required file in folder '{folder}'. Must contains at least one of {DDUF_FOLDER_REQUIRED_ENTRIES}." + ) + + +def _get_data_offset(zf: zipfile.ZipFile, info: zipfile.ZipInfo) -> int: + """ + Calculate the data offset for a file in a ZIP archive. + + Args: + zf (`zipfile.ZipFile`): + The opened ZIP file. Must be opened in read mode. + info (`zipfile.ZipInfo`): + The file info. + + Returns: + int: The offset of the file data in the ZIP archive. + """ + if zf.fp is None: + raise DDUFCorruptedFileError("ZipFile object must be opened in read mode.") + + # Step 1: Get the local file header offset + header_offset = info.header_offset + + # Step 2: Read the local file header + zf.fp.seek(header_offset) + local_file_header = zf.fp.read(30) # Fixed-size part of the local header + + if len(local_file_header) < 30: + raise DDUFCorruptedFileError("Incomplete local file header.") + + # Step 3: Parse the header fields to calculate the start of file data + # Local file header: https://en.wikipedia.org/wiki/ZIP_(file_format)#File_headers + filename_len = int.from_bytes(local_file_header[26:28], "little") + extra_field_len = int.from_bytes(local_file_header[28:30], "little") + + # Data offset is after the fixed header, filename, and extra fields + data_offset = header_offset + 30 + filename_len + extra_field_len + + return data_offset diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/serialization/_tensorflow.py b/venv/lib/python3.13/site-packages/huggingface_hub/serialization/_tensorflow.py new file mode 100644 index 0000000000000000000000000000000000000000..1173e34a28b2d7f9d879e01ffdae8ce09e9d5b5c --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/serialization/_tensorflow.py @@ -0,0 +1,92 @@ +# Copyright 2024 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains tensorflow-specific helpers.""" + +import math +import re +from typing import TYPE_CHECKING, Dict, Union + +from .. import constants +from ._base import MAX_SHARD_SIZE, StateDictSplit, split_state_dict_into_shards_factory + + +if TYPE_CHECKING: + import tensorflow as tf + + +def split_tf_state_dict_into_shards( + state_dict: Dict[str, "tf.Tensor"], + *, + filename_pattern: str = constants.TF2_WEIGHTS_FILE_PATTERN, + max_shard_size: Union[int, str] = MAX_SHARD_SIZE, +) -> StateDictSplit: + """ + Split a model state dictionary in shards so that each shard is smaller than a given size. + + The shards are determined by iterating through the `state_dict` in the order of its keys. There is no optimization + made to make each shard as close as possible to the maximum size passed. For example, if the limit is 10GB and we + have tensors of sizes [6GB, 6GB, 2GB, 6GB, 2GB, 2GB] they will get sharded as [6GB], [6+2GB], [6+2+2GB] and not + [6+2+2GB], [6+2GB], [6GB]. + + > [!WARNING] + > If one of the model's tensor is bigger than `max_shard_size`, it will end up in its own shard which will have a + > size greater than `max_shard_size`. + + Args: + state_dict (`Dict[str, Tensor]`): + The state dictionary to save. + filename_pattern (`str`, *optional*): + The pattern to generate the files names in which the model will be saved. Pattern must be a string that + can be formatted with `filename_pattern.format(suffix=...)` and must contain the keyword `suffix` + Defaults to `"tf_model{suffix}.h5"`. + max_shard_size (`int` or `str`, *optional*): + The maximum size of each shard, in bytes. Defaults to 5GB. + + Returns: + [`StateDictSplit`]: A `StateDictSplit` object containing the shards and the index to retrieve them. + """ + return split_state_dict_into_shards_factory( + state_dict, + max_shard_size=max_shard_size, + filename_pattern=filename_pattern, + get_storage_size=get_tf_storage_size, + ) + + +def get_tf_storage_size(tensor: "tf.Tensor") -> int: + # Return `math.ceil` since dtype byte size can be a float (e.g., 0.125 for tf.bool). + # Better to overestimate than underestimate. + return math.ceil(tensor.numpy().size * _dtype_byte_size_tf(tensor.dtype)) + + +def _dtype_byte_size_tf(dtype) -> float: + """ + Returns the size (in bytes) occupied by one parameter of type `dtype`. + Taken from https://github.com/huggingface/transformers/blob/74d9d0cebb0263a3f8ab9c280569170cc74651d0/src/transformers/modeling_tf_utils.py#L608. + NOTE: why not `tensor.numpy().nbytes`? + Example: + ```py + >>> _dtype_byte_size(tf.float32) + 4 + ``` + """ + import tensorflow as tf + + if dtype == tf.bool: + return 1 / 8 + bit_search = re.search(r"[^\d](\d+)$", dtype.name) + if bit_search is None: + raise ValueError(f"`dtype` is not a valid dtype: {dtype}.") + bit_size = int(bit_search.groups()[0]) + return bit_size // 8 diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/serialization/_torch.py b/venv/lib/python3.13/site-packages/huggingface_hub/serialization/_torch.py new file mode 100644 index 0000000000000000000000000000000000000000..e24d46ab4e14415104922681cd64944a33a3d9ab --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/serialization/_torch.py @@ -0,0 +1,1015 @@ +# Copyright 2024 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains pytorch-specific helpers.""" + +import importlib +import json +import os +import re +from collections import defaultdict, namedtuple +from functools import lru_cache +from pathlib import Path +from typing import TYPE_CHECKING, Any, Dict, Iterable, List, NamedTuple, Optional, Set, Tuple, Union + +from packaging import version + +from .. import constants, logging +from ._base import MAX_SHARD_SIZE, StateDictSplit, split_state_dict_into_shards_factory + + +logger = logging.get_logger(__file__) + +if TYPE_CHECKING: + import torch + +# SAVING + + +def save_torch_model( + model: "torch.nn.Module", + save_directory: Union[str, Path], + *, + filename_pattern: Optional[str] = None, + force_contiguous: bool = True, + max_shard_size: Union[int, str] = MAX_SHARD_SIZE, + metadata: Optional[Dict[str, str]] = None, + safe_serialization: bool = True, + is_main_process: bool = True, + shared_tensors_to_discard: Optional[List[str]] = None, +): + """ + Saves a given torch model to disk, handling sharding and shared tensors issues. + + See also [`save_torch_state_dict`] to save a state dict with more flexibility. + + For more information about tensor sharing, check out [this guide](https://huggingface.co/docs/safetensors/torch_shared_tensors). + + The model state dictionary is split into shards so that each shard is smaller than a given size. The shards are + saved in the `save_directory` with the given `filename_pattern`. If the model is too big to fit in a single shard, + an index file is saved in the `save_directory` to indicate where each tensor is saved. This helper uses + [`split_torch_state_dict_into_shards`] under the hood. If `safe_serialization` is `True`, the shards are saved as + safetensors (the default). Otherwise, the shards are saved as pickle. + + Before saving the model, the `save_directory` is cleaned from any previous shard files. + + > [!WARNING] + > If one of the model's tensor is bigger than `max_shard_size`, it will end up in its own shard which will have a + > size greater than `max_shard_size`. + + > [!WARNING] + > If your model is a `transformers.PreTrainedModel`, you should pass `model._tied_weights_keys` as `shared_tensors_to_discard` to properly handle shared tensors saving. This ensures the correct duplicate tensors are discarded during saving. + + Args: + model (`torch.nn.Module`): + The model to save on disk. + save_directory (`str` or `Path`): + The directory in which the model will be saved. + filename_pattern (`str`, *optional*): + The pattern to generate the files names in which the model will be saved. Pattern must be a string that + can be formatted with `filename_pattern.format(suffix=...)` and must contain the keyword `suffix` + Defaults to `"model{suffix}.safetensors"` or `pytorch_model{suffix}.bin` depending on `safe_serialization` + parameter. + force_contiguous (`boolean`, *optional*): + Forcing the state_dict to be saved as contiguous tensors. This has no effect on the correctness of the + model, but it could potentially change performance if the layout of the tensor was chosen specifically for + that reason. Defaults to `True`. + max_shard_size (`int` or `str`, *optional*): + The maximum size of each shard, in bytes. Defaults to 5GB. + metadata (`Dict[str, str]`, *optional*): + Extra information to save along with the model. Some metadata will be added for each dropped tensors. + This information will not be enough to recover the entire shared structure but might help understanding + things. + safe_serialization (`bool`, *optional*): + Whether to save as safetensors, which is the default behavior. If `False`, the shards are saved as pickle. + Safe serialization is recommended for security reasons. Saving as pickle is deprecated and will be removed + in a future version. + is_main_process (`bool`, *optional*): + Whether the process calling this is the main process or not. Useful when in distributed training like + TPUs and need to call this function from all processes. In this case, set `is_main_process=True` only on + the main process to avoid race conditions. Defaults to True. + shared_tensors_to_discard (`List[str]`, *optional*): + List of tensor names to drop when saving shared tensors. If not provided and shared tensors are + detected, it will drop the first name alphabetically. + + Example: + + ```py + >>> from huggingface_hub import save_torch_model + >>> model = ... # A PyTorch model + + # Save state dict to "path/to/folder". The model will be split into shards of 5GB each and saved as safetensors. + >>> save_torch_model(model, "path/to/folder") + + # Load model back + >>> from huggingface_hub import load_torch_model # TODO + >>> load_torch_model(model, "path/to/folder") + >>> + ``` + """ + save_torch_state_dict( + state_dict=model.state_dict(), + filename_pattern=filename_pattern, + force_contiguous=force_contiguous, + max_shard_size=max_shard_size, + metadata=metadata, + safe_serialization=safe_serialization, + save_directory=save_directory, + is_main_process=is_main_process, + shared_tensors_to_discard=shared_tensors_to_discard, + ) + + +def save_torch_state_dict( + state_dict: Dict[str, "torch.Tensor"], + save_directory: Union[str, Path], + *, + filename_pattern: Optional[str] = None, + force_contiguous: bool = True, + max_shard_size: Union[int, str] = MAX_SHARD_SIZE, + metadata: Optional[Dict[str, str]] = None, + safe_serialization: bool = True, + is_main_process: bool = True, + shared_tensors_to_discard: Optional[List[str]] = None, +) -> None: + """ + Save a model state dictionary to the disk, handling sharding and shared tensors issues. + + See also [`save_torch_model`] to directly save a PyTorch model. + + For more information about tensor sharing, check out [this guide](https://huggingface.co/docs/safetensors/torch_shared_tensors). + + The model state dictionary is split into shards so that each shard is smaller than a given size. The shards are + saved in the `save_directory` with the given `filename_pattern`. If the model is too big to fit in a single shard, + an index file is saved in the `save_directory` to indicate where each tensor is saved. This helper uses + [`split_torch_state_dict_into_shards`] under the hood. If `safe_serialization` is `True`, the shards are saved as + safetensors (the default). Otherwise, the shards are saved as pickle. + + Before saving the model, the `save_directory` is cleaned from any previous shard files. + + > [!WARNING] + > If one of the model's tensor is bigger than `max_shard_size`, it will end up in its own shard which will have a + > size greater than `max_shard_size`. + + > [!WARNING] + > If your model is a `transformers.PreTrainedModel`, you should pass `model._tied_weights_keys` as `shared_tensors_to_discard` to properly handle shared tensors saving. This ensures the correct duplicate tensors are discarded during saving. + + Args: + state_dict (`Dict[str, torch.Tensor]`): + The state dictionary to save. + save_directory (`str` or `Path`): + The directory in which the model will be saved. + filename_pattern (`str`, *optional*): + The pattern to generate the files names in which the model will be saved. Pattern must be a string that + can be formatted with `filename_pattern.format(suffix=...)` and must contain the keyword `suffix` + Defaults to `"model{suffix}.safetensors"` or `pytorch_model{suffix}.bin` depending on `safe_serialization` + parameter. + force_contiguous (`boolean`, *optional*): + Forcing the state_dict to be saved as contiguous tensors. This has no effect on the correctness of the + model, but it could potentially change performance if the layout of the tensor was chosen specifically for + that reason. Defaults to `True`. + max_shard_size (`int` or `str`, *optional*): + The maximum size of each shard, in bytes. Defaults to 5GB. + metadata (`Dict[str, str]`, *optional*): + Extra information to save along with the model. Some metadata will be added for each dropped tensors. + This information will not be enough to recover the entire shared structure but might help understanding + things. + safe_serialization (`bool`, *optional*): + Whether to save as safetensors, which is the default behavior. If `False`, the shards are saved as pickle. + Safe serialization is recommended for security reasons. Saving as pickle is deprecated and will be removed + in a future version. + is_main_process (`bool`, *optional*): + Whether the process calling this is the main process or not. Useful when in distributed training like + TPUs and need to call this function from all processes. In this case, set `is_main_process=True` only on + the main process to avoid race conditions. Defaults to True. + shared_tensors_to_discard (`List[str]`, *optional*): + List of tensor names to drop when saving shared tensors. If not provided and shared tensors are + detected, it will drop the first name alphabetically. + + Example: + + ```py + >>> from huggingface_hub import save_torch_state_dict + >>> model = ... # A PyTorch model + + # Save state dict to "path/to/folder". The model will be split into shards of 5GB each and saved as safetensors. + >>> state_dict = model_to_save.state_dict() + >>> save_torch_state_dict(state_dict, "path/to/folder") + ``` + """ + save_directory = str(save_directory) + + if filename_pattern is None: + filename_pattern = ( + constants.SAFETENSORS_WEIGHTS_FILE_PATTERN + if safe_serialization + else constants.PYTORCH_WEIGHTS_FILE_PATTERN + ) + + if metadata is None: + metadata = {} + if safe_serialization: + try: + from safetensors.torch import save_file as save_file_fn + except ImportError as e: + raise ImportError( + "Please install `safetensors` to use safe serialization. " + "You can install it with `pip install safetensors`." + ) from e + # Clean state dict for safetensors + state_dict = _clean_state_dict_for_safetensors( + state_dict, + metadata, + force_contiguous=force_contiguous, + shared_tensors_to_discard=shared_tensors_to_discard, + ) + else: + from torch import save as save_file_fn # type: ignore[assignment, no-redef] + + logger.warning( + "You are using unsafe serialization. Due to security reasons, it is recommended not to load " + "pickled models from untrusted sources. If you intend to share your model, we strongly recommend " + "using safe serialization by installing `safetensors` with `pip install safetensors`." + ) + # Split dict + state_dict_split = split_torch_state_dict_into_shards( + state_dict, filename_pattern=filename_pattern, max_shard_size=max_shard_size + ) + + # Only main process should clean up existing files to avoid race conditions in distributed environment + if is_main_process: + existing_files_regex = re.compile(filename_pattern.format(suffix=r"(-\d{5}-of-\d{5})?") + r"(\.index\.json)?") + for filename in os.listdir(save_directory): + if existing_files_regex.match(filename): + try: + logger.debug(f"Removing existing file '{filename}' from folder.") + os.remove(os.path.join(save_directory, filename)) + except Exception as e: + logger.warning( + f"Error when trying to remove existing '{filename}' from folder: {e}. Continuing..." + ) + + # Save each shard + per_file_metadata = {"format": "pt"} + if not state_dict_split.is_sharded: + per_file_metadata.update(metadata) + safe_file_kwargs = {"metadata": per_file_metadata} if safe_serialization else {} + for filename, tensors in state_dict_split.filename_to_tensors.items(): + shard = {tensor: state_dict[tensor] for tensor in tensors} + save_file_fn(shard, os.path.join(save_directory, filename), **safe_file_kwargs) # ty: ignore[invalid-argument-type] + logger.debug(f"Shard saved to {filename}") + + # Save the index (if any) + if state_dict_split.is_sharded: + index_path = filename_pattern.format(suffix="") + ".index.json" + index = { + "metadata": {**state_dict_split.metadata, **metadata}, + "weight_map": state_dict_split.tensor_to_filename, + } + with open(os.path.join(save_directory, index_path), "w") as f: + json.dump(index, f, indent=2) + logger.info( + f"The model is bigger than the maximum size per checkpoint ({max_shard_size}). " + f"Model weighs have been saved in {len(state_dict_split.filename_to_tensors)} checkpoint shards. " + f"You can find where each parameters has been saved in the index located at {index_path}." + ) + + logger.info(f"Model weights successfully saved to {save_directory}!") + + +def split_torch_state_dict_into_shards( + state_dict: Dict[str, "torch.Tensor"], + *, + filename_pattern: str = constants.SAFETENSORS_WEIGHTS_FILE_PATTERN, + max_shard_size: Union[int, str] = MAX_SHARD_SIZE, +) -> StateDictSplit: + """ + Split a model state dictionary in shards so that each shard is smaller than a given size. + + The shards are determined by iterating through the `state_dict` in the order of its keys. There is no optimization + made to make each shard as close as possible to the maximum size passed. For example, if the limit is 10GB and we + have tensors of sizes [6GB, 6GB, 2GB, 6GB, 2GB, 2GB] they will get sharded as [6GB], [6+2GB], [6+2+2GB] and not + [6+2+2GB], [6+2GB], [6GB]. + + + > [!TIP] + > To save a model state dictionary to the disk, see [`save_torch_state_dict`]. This helper uses + > `split_torch_state_dict_into_shards` under the hood. + + > [!WARNING] + > If one of the model's tensor is bigger than `max_shard_size`, it will end up in its own shard which will have a + > size greater than `max_shard_size`. + + Args: + state_dict (`Dict[str, torch.Tensor]`): + The state dictionary to save. + filename_pattern (`str`, *optional*): + The pattern to generate the files names in which the model will be saved. Pattern must be a string that + can be formatted with `filename_pattern.format(suffix=...)` and must contain the keyword `suffix` + Defaults to `"model{suffix}.safetensors"`. + max_shard_size (`int` or `str`, *optional*): + The maximum size of each shard, in bytes. Defaults to 5GB. + + Returns: + [`StateDictSplit`]: A `StateDictSplit` object containing the shards and the index to retrieve them. + + Example: + ```py + >>> import json + >>> import os + >>> from safetensors.torch import save_file as safe_save_file + >>> from huggingface_hub import split_torch_state_dict_into_shards + + >>> def save_state_dict(state_dict: Dict[str, torch.Tensor], save_directory: str): + ... state_dict_split = split_torch_state_dict_into_shards(state_dict) + ... for filename, tensors in state_dict_split.filename_to_tensors.items(): + ... shard = {tensor: state_dict[tensor] for tensor in tensors} + ... safe_save_file( + ... shard, + ... os.path.join(save_directory, filename), + ... metadata={"format": "pt"}, + ... ) + ... if state_dict_split.is_sharded: + ... index = { + ... "metadata": state_dict_split.metadata, + ... "weight_map": state_dict_split.tensor_to_filename, + ... } + ... with open(os.path.join(save_directory, "model.safetensors.index.json"), "w") as f: + ... f.write(json.dumps(index, indent=2)) + ``` + """ + return split_state_dict_into_shards_factory( + state_dict, + max_shard_size=max_shard_size, + filename_pattern=filename_pattern, + get_storage_size=get_torch_storage_size, + get_storage_id=get_torch_storage_id, + ) + + +# LOADING + + +def load_torch_model( + model: "torch.nn.Module", + checkpoint_path: Union[str, os.PathLike], + *, + strict: bool = False, + safe: bool = True, + weights_only: bool = False, + map_location: Optional[Union[str, "torch.device"]] = None, + mmap: bool = False, + filename_pattern: Optional[str] = None, +) -> NamedTuple: + """ + Load a checkpoint into a model, handling both sharded and non-sharded checkpoints. + + Args: + model (`torch.nn.Module`): + The model in which to load the checkpoint. + checkpoint_path (`str` or `os.PathLike`): + Path to either the checkpoint file or directory containing the checkpoint(s). + strict (`bool`, *optional*, defaults to `False`): + Whether to strictly enforce that the keys in the model state dict match the keys in the checkpoint. + safe (`bool`, *optional*, defaults to `True`): + If `safe` is True, the safetensors files will be loaded. If `safe` is False, the function + will first attempt to load safetensors files if they are available, otherwise it will fall back to loading + pickle files. `filename_pattern` parameter takes precedence over `safe` parameter. + weights_only (`bool`, *optional*, defaults to `False`): + If True, only loads the model weights without optimizer states and other metadata. + Only supported in PyTorch >= 1.13. + map_location (`str` or `torch.device`, *optional*): + A `torch.device` object, string or a dict specifying how to remap storage locations. It + indicates the location where all tensors should be loaded. + mmap (`bool`, *optional*, defaults to `False`): + Whether to use memory-mapped file loading. Memory mapping can improve loading performance + for large models in PyTorch >= 2.1.0 with zipfile-based checkpoints. + filename_pattern (`str`, *optional*): + The pattern to look for the index file. Pattern must be a string that + can be formatted with `filename_pattern.format(suffix=...)` and must contain the keyword `suffix` + Defaults to `"model{suffix}.safetensors"`. + Returns: + `NamedTuple`: A named tuple with `missing_keys` and `unexpected_keys` fields. + - `missing_keys` is a list of str containing the missing keys, i.e. keys that are in the model but not in the checkpoint. + - `unexpected_keys` is a list of str containing the unexpected keys, i.e. keys that are in the checkpoint but not in the model. + + Raises: + [`FileNotFoundError`](https://docs.python.org/3/library/exceptions.html#FileNotFoundError) + If the checkpoint file or directory does not exist. + [`ImportError`](https://docs.python.org/3/library/exceptions.html#ImportError) + If safetensors or torch is not installed when trying to load a .safetensors file or a PyTorch checkpoint respectively. + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If the checkpoint path is invalid or if the checkpoint format cannot be determined. + + Example: + ```python + >>> from huggingface_hub import load_torch_model + >>> model = ... # A PyTorch model + >>> load_torch_model(model, "path/to/checkpoint") + ``` + """ + checkpoint_path = Path(checkpoint_path) + + if not checkpoint_path.exists(): + raise ValueError(f"Checkpoint path {checkpoint_path} does not exist") + # 1. Check if checkpoint is a single file + if checkpoint_path.is_file(): + state_dict = load_state_dict_from_file( + checkpoint_file=checkpoint_path, + map_location=map_location, + weights_only=weights_only, + ) + return model.load_state_dict(state_dict, strict=strict) + + # 2. If not, checkpoint_path is a directory + if filename_pattern is None: + filename_pattern = constants.SAFETENSORS_WEIGHTS_FILE_PATTERN + index_path = checkpoint_path / (filename_pattern.format(suffix="") + ".index.json") + # Only fallback to pickle format if safetensors index is not found and safe is False. + if not index_path.is_file() and not safe: + filename_pattern = constants.PYTORCH_WEIGHTS_FILE_PATTERN + + index_path = checkpoint_path / (filename_pattern.format(suffix="") + ".index.json") + + if index_path.is_file(): + return _load_sharded_checkpoint( + model=model, + save_directory=checkpoint_path, + strict=strict, + weights_only=weights_only, + filename_pattern=filename_pattern, + ) + + # Look for single model file + model_files = list(checkpoint_path.glob("*.safetensors" if safe else "*.bin")) + if len(model_files) == 1: + state_dict = load_state_dict_from_file( + checkpoint_file=model_files[0], + map_location=map_location, + weights_only=weights_only, + mmap=mmap, + ) + return model.load_state_dict(state_dict, strict=strict) + + raise ValueError( + f"Directory '{checkpoint_path}' does not contain a valid checkpoint. " + "Expected either a sharded checkpoint with an index file, or a single model file." + ) + + +def _load_sharded_checkpoint( + model: "torch.nn.Module", + save_directory: os.PathLike, + *, + strict: bool = False, + weights_only: bool = False, + filename_pattern: str = constants.SAFETENSORS_WEIGHTS_FILE_PATTERN, +) -> NamedTuple: + """ + Loads a sharded checkpoint into a model. This is the same as + [`torch.nn.Module.load_state_dict`](https://pytorch.org/docs/stable/generated/torch.nn.Module.html?highlight=load_state_dict#torch.nn.Module.load_state_dict) + but for a sharded checkpoint. Each shard is loaded one by one and removed from memory after being loaded into the model. + + Args: + model (`torch.nn.Module`): + The model in which to load the checkpoint. + save_directory (`str` or `os.PathLike`): + A path to a folder containing the sharded checkpoint. + strict (`bool`, *optional*, defaults to `False`): + Whether to strictly enforce that the keys in the model state dict match the keys in the sharded checkpoint. + weights_only (`bool`, *optional*, defaults to `False`): + If True, only loads the model weights without optimizer states and other metadata. + Only supported in PyTorch >= 1.13. + filename_pattern (`str`, *optional*, defaults to `"model{suffix}.safetensors"`): + The pattern to look for the index file. Pattern must be a string that + can be formatted with `filename_pattern.format(suffix=...)` and must contain the keyword `suffix` + Defaults to `"model{suffix}.safetensors"`. + + Returns: + `NamedTuple`: A named tuple with `missing_keys` and `unexpected_keys` fields, + - `missing_keys` is a list of str containing the missing keys + - `unexpected_keys` is a list of str containing the unexpected keys + """ + + # 1. Load and validate index file + # The index file contains mapping of parameter names to shard files + index_path = filename_pattern.format(suffix="") + ".index.json" + index_file = os.path.join(save_directory, index_path) + with open(index_file, "r", encoding="utf-8") as f: + index = json.load(f) + + # 2. Validate keys if in strict mode + # This is done before loading any shards to fail fast + if strict: + _validate_keys_for_strict_loading(model, index["weight_map"].keys()) + + # 3. Load each shard using `load_state_dict` + # Get unique shard files (multiple parameters can be in same shard) + shard_files = list(set(index["weight_map"].values())) + for shard_file in shard_files: + # Load shard into memory + shard_path = os.path.join(save_directory, shard_file) + state_dict = load_state_dict_from_file( + shard_path, + map_location="cpu", + weights_only=weights_only, + ) + # Update model with parameters from this shard + model.load_state_dict(state_dict, strict=strict) + # Explicitly remove the state dict from memory + del state_dict + + # 4. Return compatibility info + loaded_keys = set(index["weight_map"].keys()) + model_keys = set(model.state_dict().keys()) + return _IncompatibleKeys( + missing_keys=list(model_keys - loaded_keys), unexpected_keys=list(loaded_keys - model_keys) + ) + + +def load_state_dict_from_file( + checkpoint_file: Union[str, os.PathLike], + map_location: Optional[Union[str, "torch.device"]] = None, + weights_only: bool = False, + mmap: bool = False, +) -> Union[Dict[str, "torch.Tensor"], Any]: + """ + Loads a checkpoint file, handling both safetensors and pickle checkpoint formats. + + Args: + checkpoint_file (`str` or `os.PathLike`): + Path to the checkpoint file to load. Can be either a safetensors or pickle (`.bin`) checkpoint. + map_location (`str` or `torch.device`, *optional*): + A `torch.device` object, string or a dict specifying how to remap storage locations. It + indicates the location where all tensors should be loaded. + weights_only (`bool`, *optional*, defaults to `False`): + If True, only loads the model weights without optimizer states and other metadata. + Only supported for pickle (`.bin`) checkpoints with PyTorch >= 1.13. Has no effect when + loading safetensors files. + mmap (`bool`, *optional*, defaults to `False`): + Whether to use memory-mapped file loading. Memory mapping can improve loading performance + for large models in PyTorch >= 2.1.0 with zipfile-based checkpoints. Has no effect when + loading safetensors files, as the `safetensors` library uses memory mapping by default. + + Returns: + `Union[Dict[str, "torch.Tensor"], Any]`: The loaded checkpoint. + - For safetensors files: always returns a dictionary mapping parameter names to tensors. + - For pickle files: returns any Python object that was pickled (commonly a state dict, but could be + an entire model, optimizer state, or any other Python object). + + Raises: + [`FileNotFoundError`](https://docs.python.org/3/library/exceptions.html#FileNotFoundError) + If the checkpoint file does not exist. + [`ImportError`](https://docs.python.org/3/library/exceptions.html#ImportError) + If safetensors or torch is not installed when trying to load a .safetensors file or a PyTorch checkpoint respectively. + [`OSError`](https://docs.python.org/3/library/exceptions.html#OSError) + If the checkpoint file format is invalid or if git-lfs files are not properly downloaded. + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If the checkpoint file path is empty or invalid. + + Example: + ```python + >>> from huggingface_hub import load_state_dict_from_file + + # Load a PyTorch checkpoint + >>> state_dict = load_state_dict_from_file("path/to/model.bin", map_location="cpu") + >>> model.load_state_dict(state_dict) + + # Load a safetensors checkpoint + >>> state_dict = load_state_dict_from_file("path/to/model.safetensors") + >>> model.load_state_dict(state_dict) + ``` + """ + checkpoint_path = Path(checkpoint_file) + + # Check if file exists and is a regular file (not a directory) + if not checkpoint_path.is_file(): + raise FileNotFoundError( + f"No checkpoint file found at '{checkpoint_path}'. Please verify the path is correct and " + "the file has been properly downloaded." + ) + + # Load safetensors checkpoint + if checkpoint_path.suffix == ".safetensors": + try: + from safetensors import safe_open + from safetensors.torch import load_file + except ImportError as e: + raise ImportError( + "Please install `safetensors` to load safetensors checkpoint. " + "You can install it with `pip install safetensors`." + ) from e + + # Check format of the archive + with safe_open(checkpoint_file, framework="pt") as f: # type: ignore[attr-defined] + metadata = f.metadata() + # see comment: https://github.com/huggingface/transformers/blob/3d213b57fe74302e5902d68ed9478c3ad1aaa713/src/transformers/modeling_utils.py#L3966 + if metadata is not None and metadata.get("format") not in ["pt", "mlx"]: + raise OSError( + f"The safetensors archive passed at {checkpoint_file} does not contain the valid metadata. Make sure " + "you save your model with the `save_torch_model` method." + ) + device = str(map_location.type) if map_location is not None and hasattr(map_location, "type") else map_location + # meta device is not supported with safetensors, falling back to CPU + if device == "meta": + logger.warning("Meta device is not supported with safetensors. Falling back to CPU device.") + device = "cpu" + return load_file(checkpoint_file, device=device) # type: ignore[arg-type] + # Otherwise, load from pickle + try: + import torch + from torch import load + except ImportError as e: + raise ImportError( + "Please install `torch` to load torch tensors. You can install it with `pip install torch`." + ) from e + # Add additional kwargs, mmap is only supported in torch >= 2.1.0 + additional_kwargs = {} + if version.parse(torch.__version__) >= version.parse("2.1.0"): + additional_kwargs["mmap"] = mmap + + # weights_only is only supported in torch >= 1.13.0 + if version.parse(torch.__version__) >= version.parse("1.13.0"): + additional_kwargs["weights_only"] = weights_only + + return load( + checkpoint_file, + map_location=map_location, + **additional_kwargs, + ) + + +# HELPERS + + +def _validate_keys_for_strict_loading( + model: "torch.nn.Module", + loaded_keys: Iterable[str], +) -> None: + """ + Validate that model keys match loaded keys when strict loading is enabled. + + Args: + model: The PyTorch model being loaded + loaded_keys: The keys present in the checkpoint + + Raises: + RuntimeError: If there are missing or unexpected keys in strict mode + """ + loaded_keys_set = set(loaded_keys) + model_keys = set(model.state_dict().keys()) + missing_keys = model_keys - loaded_keys_set # Keys in model but not in checkpoint + unexpected_keys = loaded_keys_set - model_keys # Keys in checkpoint but not in model + + if missing_keys or unexpected_keys: + error_message = f"Error(s) in loading state_dict for {model.__class__.__name__}" + if missing_keys: + str_missing_keys = ",".join([f'"{k}"' for k in sorted(missing_keys)]) + error_message += f"\nMissing key(s): {str_missing_keys}." + if unexpected_keys: + str_unexpected_keys = ",".join([f'"{k}"' for k in sorted(unexpected_keys)]) + error_message += f"\nUnexpected key(s): {str_unexpected_keys}." + raise RuntimeError(error_message) + + +def _get_unique_id(tensor: "torch.Tensor") -> Union[int, Tuple[Any, ...]]: + """Returns a unique id for plain tensor + or a (potentially nested) Tuple of unique id for the flattened Tensor + if the input is a wrapper tensor subclass Tensor + """ + + try: + from torch.distributed.tensor import DTensor + + if isinstance(tensor, DTensor): + local_tensor = tensor.to_local() + return local_tensor.storage().data_ptr() + except ImportError: + pass + + try: + # for torch 2.1 and above we can also handle tensor subclasses + from torch.utils._python_dispatch import is_traceable_wrapper_subclass + + if is_traceable_wrapper_subclass(tensor): + attrs, _ = tensor.__tensor_flatten__() # type: ignore[attr-defined] + return tuple(_get_unique_id(getattr(tensor, attr)) for attr in attrs) + + except ImportError: + # for torch version less than 2.1, we can fallback to original implementation + pass + + if tensor.device.type == "xla" and is_torch_tpu_available(): + # NOTE: xla tensors dont have storage + # use some other unique id to distinguish. + # this is a XLA tensor, it must be created using torch_xla's + # device. So the following import is safe: + import torch_xla # type: ignore[import] + + unique_id = torch_xla._XLAC._xla_get_tensor_id(tensor) + else: + unique_id = storage_ptr(tensor) + + return unique_id + + +def get_torch_storage_id(tensor: "torch.Tensor") -> Optional[Tuple["torch.device", Union[int, Tuple[Any, ...]], int]]: + """ + Return unique identifier to a tensor storage. + + Multiple different tensors can share the same underlying storage. This identifier is + guaranteed to be unique and constant for this tensor's storage during its lifetime. Two tensor storages with + non-overlapping lifetimes may have the same id. + In the case of meta tensors, we return None since we can't tell if they share the same storage. + + Taken from https://github.com/huggingface/transformers/blob/1ecf5f7c982d761b4daaa96719d162c324187c64/src/transformers/pytorch_utils.py#L278. + """ + if tensor.device.type == "meta": + return None + else: + return tensor.device, _get_unique_id(tensor), get_torch_storage_size(tensor) + + +def get_torch_storage_size(tensor: "torch.Tensor") -> int: + """ + Taken from https://github.com/huggingface/safetensors/blob/08db34094e9e59e2f9218f2df133b7b4aaff5a99/bindings/python/py_src/safetensors/torch.py#L31C1-L41C59 + """ + try: + from torch.distributed.tensor import DTensor + + if isinstance(tensor, DTensor): + # this returns the size of the FULL tensor in bytes + return tensor.nbytes + except ImportError: + pass + + try: + # for torch 2.1 and above we can also handle tensor subclasses + from torch.utils._python_dispatch import is_traceable_wrapper_subclass + + if is_traceable_wrapper_subclass(tensor): + attrs, _ = tensor.__tensor_flatten__() # type: ignore[attr-defined] + return sum(get_torch_storage_size(getattr(tensor, attr)) for attr in attrs) + except ImportError: + # for torch version less than 2.1, we can fallback to original implementation + pass + + try: + return tensor.untyped_storage().nbytes() + except AttributeError: + # Fallback for torch==1.10 + try: + return tensor.storage().size() * _get_dtype_size(tensor.dtype) + except NotImplementedError: + # Fallback for meta storage + # On torch >=2.0 this is the tensor size + return tensor.nelement() * _get_dtype_size(tensor.dtype) + + +@lru_cache() +def is_torch_tpu_available(check_device=True): + """ + Checks if `torch_xla` is installed and potentially if a TPU is in the environment + + Taken from https://github.com/huggingface/transformers/blob/1ecf5f7c982d761b4daaa96719d162c324187c64/src/transformers/utils/import_utils.py#L463. + """ + if importlib.util.find_spec("torch_xla") is not None: + if check_device: + # We need to check if `xla_device` can be found, will raise a RuntimeError if not + try: + import torch_xla.core.xla_model as xm # type: ignore[import] + + _ = xm.xla_device() + return True + except RuntimeError: + return False + return True + return False + + +def storage_ptr(tensor: "torch.Tensor") -> Union[int, Tuple[Any, ...]]: + """ + Taken from https://github.com/huggingface/safetensors/blob/079781fd0dc455ba0fe851e2b4507c33d0c0d407/bindings/python/py_src/safetensors/torch.py#L11. + """ + try: + # for torch 2.1 and above we can also handle tensor subclasses + from torch.utils._python_dispatch import is_traceable_wrapper_subclass + + if is_traceable_wrapper_subclass(tensor): + return _get_unique_id(tensor) # type: ignore + except ImportError: + # for torch version less than 2.1, we can fallback to original implementation + pass + + try: + return tensor.untyped_storage().data_ptr() + except Exception: + # Fallback for torch==1.10 + try: + return tensor.storage().data_ptr() + except NotImplementedError: + # Fallback for meta storage + return 0 + + +def _clean_state_dict_for_safetensors( + state_dict: Dict[str, "torch.Tensor"], + metadata: Dict[str, str], + force_contiguous: bool = True, + shared_tensors_to_discard: Optional[List[str]] = None, +): + """Remove shared tensors from state_dict and update metadata accordingly (for reloading). + + Warning: `state_dict` and `metadata` are mutated in-place! + + Taken from https://github.com/huggingface/safetensors/blob/079781fd0dc455ba0fe851e2b4507c33d0c0d407/bindings/python/py_src/safetensors/torch.py#L155. + """ + to_removes = _remove_duplicate_names(state_dict, discard_names=shared_tensors_to_discard) + for kept_name, to_remove_group in to_removes.items(): + for to_remove in to_remove_group: + if metadata is None: + metadata = {} + + if to_remove not in metadata: + # Do not override user data + metadata[to_remove] = kept_name + del state_dict[to_remove] + if force_contiguous: + state_dict = {k: v.contiguous() for k, v in state_dict.items()} + return state_dict + + +def _end_ptr(tensor: "torch.Tensor") -> int: + """ + Taken from https://github.com/huggingface/safetensors/blob/079781fd0dc455ba0fe851e2b4507c33d0c0d407/bindings/python/py_src/safetensors/torch.py#L23. + """ + if tensor.nelement(): + stop = tensor.view(-1)[-1].data_ptr() + _get_dtype_size(tensor.dtype) + else: + stop = tensor.data_ptr() + return stop + + +def _filter_shared_not_shared(tensors: List[Set[str]], state_dict: Dict[str, "torch.Tensor"]) -> List[Set[str]]: + """ + Taken from https://github.com/huggingface/safetensors/blob/079781fd0dc455ba0fe851e2b4507c33d0c0d407/bindings/python/py_src/safetensors/torch.py#L44 + """ + filtered_tensors = [] + for shared in tensors: + if len(shared) < 2: + filtered_tensors.append(shared) + continue + + areas = [] + for name in shared: + tensor = state_dict[name] + areas.append((tensor.data_ptr(), _end_ptr(tensor), name)) + areas.sort() + + _, last_stop, last_name = areas[0] + filtered_tensors.append({last_name}) + for start, stop, name in areas[1:]: + if start >= last_stop: + filtered_tensors.append({name}) + else: + filtered_tensors[-1].add(name) + last_stop = stop + + return filtered_tensors + + +def _find_shared_tensors(state_dict: Dict[str, "torch.Tensor"]) -> List[Set[str]]: + """ + Taken from https://github.com/huggingface/safetensors/blob/079781fd0dc455ba0fe851e2b4507c33d0c0d407/bindings/python/py_src/safetensors/torch.py#L69. + """ + import torch + + tensors_dict = defaultdict(set) + for k, v in state_dict.items(): + if v.device != torch.device("meta") and storage_ptr(v) != 0 and get_torch_storage_size(v) != 0: + # Need to add device as key because of multiple GPU. + tensors_dict[(v.device, storage_ptr(v), get_torch_storage_size(v))].add(k) + tensors = list(sorted(tensors_dict.values())) + tensors = _filter_shared_not_shared(tensors, state_dict) + return tensors + + +def _is_complete(tensor: "torch.Tensor") -> bool: + """ + Taken from https://github.com/huggingface/safetensors/blob/079781fd0dc455ba0fe851e2b4507c33d0c0d407/bindings/python/py_src/safetensors/torch.py#L80 + """ + try: + # for torch 2.1 and above we can also handle tensor subclasses + from torch.utils._python_dispatch import is_traceable_wrapper_subclass + + if is_traceable_wrapper_subclass(tensor): + attrs, _ = tensor.__tensor_flatten__() # type: ignore[attr-defined] + return all(_is_complete(getattr(tensor, attr)) for attr in attrs) + except ImportError: + # for torch version less than 2.1, we can fallback to original implementation + pass + + return tensor.data_ptr() == storage_ptr(tensor) and tensor.nelement() * _get_dtype_size( + tensor.dtype + ) == get_torch_storage_size(tensor) + + +def _remove_duplicate_names( + state_dict: Dict[str, "torch.Tensor"], + *, + preferred_names: Optional[List[str]] = None, + discard_names: Optional[List[str]] = None, +) -> Dict[str, List[str]]: + """ + Taken from https://github.com/huggingface/safetensors/blob/079781fd0dc455ba0fe851e2b4507c33d0c0d407/bindings/python/py_src/safetensors/torch.py#L80 + """ + if preferred_names is None: + preferred_names = [] + unique_preferred_names = set(preferred_names) + if discard_names is None: + discard_names = [] + unique_discard_names = set(discard_names) + + shareds = _find_shared_tensors(state_dict) + to_remove = defaultdict(list) + for shared in shareds: + complete_names = set([name for name in shared if _is_complete(state_dict[name])]) + if not complete_names: + raise RuntimeError( + "Error while trying to find names to remove to save state dict, but found no suitable name to keep" + f" for saving amongst: {shared}. None is covering the entire storage. Refusing to save/load the model" + " since you could be storing much more memory than needed. Please refer to" + " https://huggingface.co/docs/safetensors/torch_shared_tensors for more information. Or open an" + " issue." + ) + + keep_name = sorted(list(complete_names))[0] + + # Mechanism to preferentially select keys to keep + # coming from the on-disk file to allow + # loading models saved with a different choice + # of keep_name + preferred = complete_names.difference(unique_discard_names) + if preferred: + keep_name = sorted(list(preferred))[0] + + if unique_preferred_names: + preferred = unique_preferred_names.intersection(complete_names) + if preferred: + keep_name = sorted(list(preferred))[0] + for name in sorted(shared): + if name != keep_name: + to_remove[keep_name].append(name) + return to_remove + + +@lru_cache() +def _get_dtype_size(dtype: "torch.dtype") -> int: + """ + Taken from https://github.com/huggingface/safetensors/blob/08db34094e9e59e2f9218f2df133b7b4aaff5a99/bindings/python/py_src/safetensors/torch.py#L344 + """ + import torch + + # torch.float8 formats require 2.1; we do not support these dtypes on earlier versions + _float8_e4m3fn = getattr(torch, "float8_e4m3fn", None) + _float8_e5m2 = getattr(torch, "float8_e5m2", None) + _SIZE = { + torch.int64: 8, + torch.float32: 4, + torch.int32: 4, + torch.bfloat16: 2, + torch.float16: 2, + torch.int16: 2, + torch.uint8: 1, + torch.int8: 1, + torch.bool: 1, + torch.float64: 8, + _float8_e4m3fn: 1, + _float8_e5m2: 1, + } + return _SIZE[dtype] + + +class _IncompatibleKeys(namedtuple("IncompatibleKeys", ["missing_keys", "unexpected_keys"])): + """ + This is used to report missing and unexpected keys in the state dict. + Taken from https://github.com/pytorch/pytorch/blob/main/torch/nn/modules/module.py#L52. + + """ + + def __repr__(self) -> str: + if not self.missing_keys and not self.unexpected_keys: + return "" + return super().__repr__() + + __str__ = __repr__ diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/templates/datasetcard_template.md b/venv/lib/python3.13/site-packages/huggingface_hub/templates/datasetcard_template.md new file mode 100644 index 0000000000000000000000000000000000000000..9af29ebbed93653ec74a8952e314e7554323ef15 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/templates/datasetcard_template.md @@ -0,0 +1,143 @@ +--- +# For reference on dataset card metadata, see the spec: https://github.com/huggingface/hub-docs/blob/main/datasetcard.md?plain=1 +# Doc / guide: https://huggingface.co/docs/hub/datasets-cards +{{ card_data }} +--- + +# Dataset Card for {{ pretty_name | default("Dataset Name", true) }} + + + +{{ dataset_summary | default("", true) }} + +## Dataset Details + +### Dataset Description + + + +{{ dataset_description | default("", true) }} + +- **Curated by:** {{ curators | default("[More Information Needed]", true)}} +- **Funded by [optional]:** {{ funded_by | default("[More Information Needed]", true)}} +- **Shared by [optional]:** {{ shared_by | default("[More Information Needed]", true)}} +- **Language(s) (NLP):** {{ language | default("[More Information Needed]", true)}} +- **License:** {{ license | default("[More Information Needed]", true)}} + +### Dataset Sources [optional] + + + +- **Repository:** {{ repo | default("[More Information Needed]", true)}} +- **Paper [optional]:** {{ paper | default("[More Information Needed]", true)}} +- **Demo [optional]:** {{ demo | default("[More Information Needed]", true)}} + +## Uses + + + +### Direct Use + + + +{{ direct_use | default("[More Information Needed]", true)}} + +### Out-of-Scope Use + + + +{{ out_of_scope_use | default("[More Information Needed]", true)}} + +## Dataset Structure + + + +{{ dataset_structure | default("[More Information Needed]", true)}} + +## Dataset Creation + +### Curation Rationale + + + +{{ curation_rationale_section | default("[More Information Needed]", true)}} + +### Source Data + + + +#### Data Collection and Processing + + + +{{ data_collection_and_processing_section | default("[More Information Needed]", true)}} + +#### Who are the source data producers? + + + +{{ source_data_producers_section | default("[More Information Needed]", true)}} + +### Annotations [optional] + + + +#### Annotation process + + + +{{ annotation_process_section | default("[More Information Needed]", true)}} + +#### Who are the annotators? + + + +{{ who_are_annotators_section | default("[More Information Needed]", true)}} + +#### Personal and Sensitive Information + + + +{{ personal_and_sensitive_information | default("[More Information Needed]", true)}} + +## Bias, Risks, and Limitations + + + +{{ bias_risks_limitations | default("[More Information Needed]", true)}} + +### Recommendations + + + +{{ bias_recommendations | default("Users should be made aware of the risks, biases and limitations of the dataset. More information needed for further recommendations.", true)}} + +## Citation [optional] + + + +**BibTeX:** + +{{ citation_bibtex | default("[More Information Needed]", true)}} + +**APA:** + +{{ citation_apa | default("[More Information Needed]", true)}} + +## Glossary [optional] + + + +{{ glossary | default("[More Information Needed]", true)}} + +## More Information [optional] + +{{ more_information | default("[More Information Needed]", true)}} + +## Dataset Card Authors [optional] + +{{ dataset_card_authors | default("[More Information Needed]", true)}} + +## Dataset Card Contact + +{{ dataset_card_contact | default("[More Information Needed]", true)}} diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/templates/modelcard_template.md b/venv/lib/python3.13/site-packages/huggingface_hub/templates/modelcard_template.md new file mode 100644 index 0000000000000000000000000000000000000000..79ca15e4547debac763b390ef8e4b715e6f6403f --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/templates/modelcard_template.md @@ -0,0 +1,200 @@ +--- +# For reference on model card metadata, see the spec: https://github.com/huggingface/hub-docs/blob/main/modelcard.md?plain=1 +# Doc / guide: https://huggingface.co/docs/hub/model-cards +{{ card_data }} +--- + +# Model Card for {{ model_id | default("Model ID", true) }} + + + +{{ model_summary | default("", true) }} + +## Model Details + +### Model Description + + + +{{ model_description | default("", true) }} + +- **Developed by:** {{ developers | default("[More Information Needed]", true)}} +- **Funded by [optional]:** {{ funded_by | default("[More Information Needed]", true)}} +- **Shared by [optional]:** {{ shared_by | default("[More Information Needed]", true)}} +- **Model type:** {{ model_type | default("[More Information Needed]", true)}} +- **Language(s) (NLP):** {{ language | default("[More Information Needed]", true)}} +- **License:** {{ license | default("[More Information Needed]", true)}} +- **Finetuned from model [optional]:** {{ base_model | default("[More Information Needed]", true)}} + +### Model Sources [optional] + + + +- **Repository:** {{ repo | default("[More Information Needed]", true)}} +- **Paper [optional]:** {{ paper | default("[More Information Needed]", true)}} +- **Demo [optional]:** {{ demo | default("[More Information Needed]", true)}} + +## Uses + + + +### Direct Use + + + +{{ direct_use | default("[More Information Needed]", true)}} + +### Downstream Use [optional] + + + +{{ downstream_use | default("[More Information Needed]", true)}} + +### Out-of-Scope Use + + + +{{ out_of_scope_use | default("[More Information Needed]", true)}} + +## Bias, Risks, and Limitations + + + +{{ bias_risks_limitations | default("[More Information Needed]", true)}} + +### Recommendations + + + +{{ bias_recommendations | default("Users (both direct and downstream) should be made aware of the risks, biases and limitations of the model. More information needed for further recommendations.", true)}} + +## How to Get Started with the Model + +Use the code below to get started with the model. + +{{ get_started_code | default("[More Information Needed]", true)}} + +## Training Details + +### Training Data + + + +{{ training_data | default("[More Information Needed]", true)}} + +### Training Procedure + + + +#### Preprocessing [optional] + +{{ preprocessing | default("[More Information Needed]", true)}} + + +#### Training Hyperparameters + +- **Training regime:** {{ training_regime | default("[More Information Needed]", true)}} + +#### Speeds, Sizes, Times [optional] + + + +{{ speeds_sizes_times | default("[More Information Needed]", true)}} + +## Evaluation + + + +### Testing Data, Factors & Metrics + +#### Testing Data + + + +{{ testing_data | default("[More Information Needed]", true)}} + +#### Factors + + + +{{ testing_factors | default("[More Information Needed]", true)}} + +#### Metrics + + + +{{ testing_metrics | default("[More Information Needed]", true)}} + +### Results + +{{ results | default("[More Information Needed]", true)}} + +#### Summary + +{{ results_summary | default("", true) }} + +## Model Examination [optional] + + + +{{ model_examination | default("[More Information Needed]", true)}} + +## Environmental Impact + + + +Carbon emissions can be estimated using the [Machine Learning Impact calculator](https://mlco2.github.io/impact#compute) presented in [Lacoste et al. (2019)](https://arxiv.org/abs/1910.09700). + +- **Hardware Type:** {{ hardware_type | default("[More Information Needed]", true)}} +- **Hours used:** {{ hours_used | default("[More Information Needed]", true)}} +- **Cloud Provider:** {{ cloud_provider | default("[More Information Needed]", true)}} +- **Compute Region:** {{ cloud_region | default("[More Information Needed]", true)}} +- **Carbon Emitted:** {{ co2_emitted | default("[More Information Needed]", true)}} + +## Technical Specifications [optional] + +### Model Architecture and Objective + +{{ model_specs | default("[More Information Needed]", true)}} + +### Compute Infrastructure + +{{ compute_infrastructure | default("[More Information Needed]", true)}} + +#### Hardware + +{{ hardware_requirements | default("[More Information Needed]", true)}} + +#### Software + +{{ software | default("[More Information Needed]", true)}} + +## Citation [optional] + + + +**BibTeX:** + +{{ citation_bibtex | default("[More Information Needed]", true)}} + +**APA:** + +{{ citation_apa | default("[More Information Needed]", true)}} + +## Glossary [optional] + + + +{{ glossary | default("[More Information Needed]", true)}} + +## More Information [optional] + +{{ more_information | default("[More Information Needed]", true)}} + +## Model Card Authors [optional] + +{{ model_card_authors | default("[More Information Needed]", true)}} + +## Model Card Contact + +{{ model_card_contact | default("[More Information Needed]", true)}} diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/utils/__init__.py b/venv/lib/python3.13/site-packages/huggingface_hub/utils/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..992eac104bd80de97444003172e926d5ad4522a0 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/utils/__init__.py @@ -0,0 +1,117 @@ +# coding=utf-8 +# Copyright 2021 The HuggingFace Inc. team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +# ruff: noqa: F401 +from huggingface_hub.errors import ( + BadRequestError, + CacheNotFound, + CorruptedCacheException, + DisabledRepoError, + EntryNotFoundError, + FileMetadataError, + GatedRepoError, + HfHubHTTPError, + HFValidationError, + LocalEntryNotFoundError, + LocalTokenNotFoundError, + NotASafetensorsRepoError, + OfflineModeIsEnabled, + RepositoryNotFoundError, + RevisionNotFoundError, + SafetensorsParsingError, +) + +from . import tqdm as _tqdm # _tqdm is the module +from ._auth import get_stored_tokens, get_token +from ._cache_assets import cached_assets_path +from ._cache_manager import ( + CachedFileInfo, + CachedRepoInfo, + CachedRevisionInfo, + DeleteCacheStrategy, + HFCacheInfo, + scan_cache_dir, +) +from ._chunk_utils import chunk_iterable +from ._datetime import parse_datetime +from ._experimental import experimental +from ._fixes import SoftTemporaryDirectory, WeakFileLock, yaml_dump +from ._git_credential import list_credential_helpers, set_git_credential, unset_git_credential +from ._headers import build_hf_headers, get_token_to_send +from ._hf_folder import HfFolder +from ._http import ( + configure_http_backend, + fix_hf_endpoint_in_url, + get_session, + hf_raise_for_status, + http_backoff, + reset_sessions, +) +from ._pagination import paginate +from ._paths import DEFAULT_IGNORE_PATTERNS, FORBIDDEN_FOLDERS, filter_repo_objects +from ._runtime import ( + dump_environment_info, + get_aiohttp_version, + get_fastai_version, + get_fastapi_version, + get_fastcore_version, + get_gradio_version, + get_graphviz_version, + get_hf_hub_version, + get_hf_transfer_version, + get_jinja_version, + get_numpy_version, + get_pillow_version, + get_pydantic_version, + get_pydot_version, + get_python_version, + get_tensorboard_version, + get_tf_version, + get_torch_version, + is_aiohttp_available, + is_colab_enterprise, + is_fastai_available, + is_fastapi_available, + is_fastcore_available, + is_google_colab, + is_gradio_available, + is_graphviz_available, + is_hf_transfer_available, + is_jinja_available, + is_notebook, + is_numpy_available, + is_package_available, + is_pillow_available, + is_pydantic_available, + is_pydot_available, + is_safetensors_available, + is_tensorboard_available, + is_tf_available, + is_torch_available, +) +from ._safetensors import SafetensorsFileMetadata, SafetensorsRepoMetadata, TensorInfo +from ._subprocess import capture_output, run_interactive_subprocess, run_subprocess +from ._telemetry import send_telemetry +from ._typing import is_jsonable, is_simple_optional_type, unwrap_simple_optional_type +from ._validators import smoothly_deprecate_use_auth_token, validate_hf_hub_args, validate_repo_id +from ._xet import ( + XetConnectionInfo, + XetFileData, + XetTokenType, + fetch_xet_connection_info_from_repo_info, + parse_xet_file_data_from_response, + refresh_xet_connection_info, +) +from .tqdm import are_progress_bars_disabled, disable_progress_bars, enable_progress_bars, tqdm, tqdm_stream_file diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/utils/_auth.py b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_auth.py new file mode 100644 index 0000000000000000000000000000000000000000..72be4dedbd94421ee2b4b2ba1073569d71b50569 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_auth.py @@ -0,0 +1,214 @@ +# Copyright 2023 The HuggingFace Team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains an helper to get the token from machine (env variable, secret or config file).""" + +import configparser +import logging +import os +import warnings +from pathlib import Path +from threading import Lock +from typing import Dict, Optional + +from .. import constants +from ._runtime import is_colab_enterprise, is_google_colab + + +_IS_GOOGLE_COLAB_CHECKED = False +_GOOGLE_COLAB_SECRET_LOCK = Lock() +_GOOGLE_COLAB_SECRET: Optional[str] = None + +logger = logging.getLogger(__name__) + + +def get_token() -> Optional[str]: + """ + Get token if user is logged in. + + Note: in most cases, you should use [`huggingface_hub.utils.build_hf_headers`] instead. This method is only useful + if you want to retrieve the token for other purposes than sending an HTTP request. + + Token is retrieved in priority from the `HF_TOKEN` environment variable. Otherwise, we read the token file located + in the Hugging Face home folder. Returns None if user is not logged in. To log in, use [`login`] or + `hf auth login`. + + Returns: + `str` or `None`: The token, `None` if it doesn't exist. + """ + return _get_token_from_google_colab() or _get_token_from_environment() or _get_token_from_file() + + +def _get_token_from_google_colab() -> Optional[str]: + """Get token from Google Colab secrets vault using `google.colab.userdata.get(...)`. + + Token is read from the vault only once per session and then stored in a global variable to avoid re-requesting + access to the vault. + """ + # If it's not a Google Colab or it's Colab Enterprise, fallback to environment variable or token file authentication + if not is_google_colab() or is_colab_enterprise(): + return None + + # `google.colab.userdata` is not thread-safe + # This can lead to a deadlock if multiple threads try to access it at the same time + # (typically when using `snapshot_download`) + # => use a lock + # See https://github.com/huggingface/huggingface_hub/issues/1952 for more details. + with _GOOGLE_COLAB_SECRET_LOCK: + global _GOOGLE_COLAB_SECRET + global _IS_GOOGLE_COLAB_CHECKED + + if _IS_GOOGLE_COLAB_CHECKED: # request access only once + return _GOOGLE_COLAB_SECRET + + try: + from google.colab import userdata # type: ignore + from google.colab.errors import Error as ColabError # type: ignore + except ImportError: + return None + + try: + token = userdata.get("HF_TOKEN") + _GOOGLE_COLAB_SECRET = _clean_token(token) + except userdata.NotebookAccessError: + # Means the user has a secret call `HF_TOKEN` and got a popup "please grand access to HF_TOKEN" and refused it + # => warn user but ignore error => do not re-request access to user + warnings.warn( + "\nAccess to the secret `HF_TOKEN` has not been granted on this notebook." + "\nYou will not be requested again." + "\nPlease restart the session if you want to be prompted again." + ) + _GOOGLE_COLAB_SECRET = None + except userdata.SecretNotFoundError: + # Means the user did not define a `HF_TOKEN` secret => warn + warnings.warn( + "\nThe secret `HF_TOKEN` does not exist in your Colab secrets." + "\nTo authenticate with the Hugging Face Hub, create a token in your settings tab " + "(https://huggingface.co/settings/tokens), set it as secret in your Google Colab and restart your session." + "\nYou will be able to reuse this secret in all of your notebooks." + "\nPlease note that authentication is recommended but still optional to access public models or datasets." + ) + _GOOGLE_COLAB_SECRET = None + except ColabError as e: + # Something happen but we don't know what => recommend to open a GitHub issue + warnings.warn( + f"\nError while fetching `HF_TOKEN` secret value from your vault: '{str(e)}'." + "\nYou are not authenticated with the Hugging Face Hub in this notebook." + "\nIf the error persists, please let us know by opening an issue on GitHub " + "(https://github.com/huggingface/huggingface_hub/issues/new)." + ) + _GOOGLE_COLAB_SECRET = None + + _IS_GOOGLE_COLAB_CHECKED = True + return _GOOGLE_COLAB_SECRET + + +def _get_token_from_environment() -> Optional[str]: + # `HF_TOKEN` has priority (keep `HUGGING_FACE_HUB_TOKEN` for backward compatibility) + return _clean_token(os.environ.get("HF_TOKEN") or os.environ.get("HUGGING_FACE_HUB_TOKEN")) + + +def _get_token_from_file() -> Optional[str]: + try: + return _clean_token(Path(constants.HF_TOKEN_PATH).read_text()) + except FileNotFoundError: + return None + + +def get_stored_tokens() -> Dict[str, str]: + """ + Returns the parsed INI file containing the access tokens. + The file is located at `HF_STORED_TOKENS_PATH`, defaulting to `~/.cache/huggingface/stored_tokens`. + If the file does not exist, an empty dictionary is returned. + + Returns: `Dict[str, str]` + Key is the token name and value is the token. + """ + tokens_path = Path(constants.HF_STORED_TOKENS_PATH) + if not tokens_path.exists(): + stored_tokens = {} + config = configparser.ConfigParser() + try: + config.read(tokens_path) + stored_tokens = {token_name: config.get(token_name, "hf_token") for token_name in config.sections()} + except configparser.Error as e: + logger.error(f"Error parsing stored tokens file: {e}") + stored_tokens = {} + return stored_tokens + + +def _save_stored_tokens(stored_tokens: Dict[str, str]) -> None: + """ + Saves the given configuration to the stored tokens file. + + Args: + stored_tokens (`Dict[str, str]`): + The stored tokens to save. Key is the token name and value is the token. + """ + stored_tokens_path = Path(constants.HF_STORED_TOKENS_PATH) + + # Write the stored tokens into an INI file + config = configparser.ConfigParser() + for token_name in sorted(stored_tokens.keys()): + config.add_section(token_name) + config.set(token_name, "hf_token", stored_tokens[token_name]) + + stored_tokens_path.parent.mkdir(parents=True, exist_ok=True) + with stored_tokens_path.open("w") as config_file: + config.write(config_file) + + +def _get_token_by_name(token_name: str) -> Optional[str]: + """ + Get the token by name. + + Args: + token_name (`str`): + The name of the token to get. + + Returns: + `str` or `None`: The token, `None` if it doesn't exist. + + """ + stored_tokens = get_stored_tokens() + if token_name not in stored_tokens: + return None + return _clean_token(stored_tokens[token_name]) + + +def _save_token(token: str, token_name: str) -> None: + """ + Save the given token. + + If the stored tokens file does not exist, it will be created. + Args: + token (`str`): + The token to save. + token_name (`str`): + The name of the token. + """ + tokens_path = Path(constants.HF_STORED_TOKENS_PATH) + stored_tokens = get_stored_tokens() + stored_tokens[token_name] = token + _save_stored_tokens(stored_tokens) + logger.info(f"The token `{token_name}` has been saved to {tokens_path}") + + +def _clean_token(token: Optional[str]) -> Optional[str]: + """Clean token by removing trailing and leading spaces and newlines. + + If token is an empty string, return None. + """ + if token is None: + return None + return token.replace("\r", "").replace("\n", "").strip() or None diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/utils/_cache_assets.py b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_cache_assets.py new file mode 100644 index 0000000000000000000000000000000000000000..e5d435df9b0bb0c67c0bcb5ef65711e9aef367f6 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_cache_assets.py @@ -0,0 +1,135 @@ +# coding=utf-8 +# Copyright 2019-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from pathlib import Path +from typing import Union + +from ..constants import HF_ASSETS_CACHE + + +def cached_assets_path( + library_name: str, + namespace: str = "default", + subfolder: str = "default", + *, + assets_dir: Union[str, Path, None] = None, +): + """Return a folder path to cache arbitrary files. + + `huggingface_hub` provides a canonical folder path to store assets. This is the + recommended way to integrate cache in a downstream library as it will benefit from + the builtins tools to scan and delete the cache properly. + + The distinction is made between files cached from the Hub and assets. Files from the + Hub are cached in a git-aware manner and entirely managed by `huggingface_hub`. See + [related documentation](https://huggingface.co/docs/huggingface_hub/how-to-cache). + All other files that a downstream library caches are considered to be "assets" + (files downloaded from external sources, extracted from a .tar archive, preprocessed + for training,...). + + Once the folder path is generated, it is guaranteed to exist and to be a directory. + The path is based on 3 levels of depth: the library name, a namespace and a + subfolder. Those 3 levels grants flexibility while allowing `huggingface_hub` to + expect folders when scanning/deleting parts of the assets cache. Within a library, + it is expected that all namespaces share the same subset of subfolder names but this + is not a mandatory rule. The downstream library has then full control on which file + structure to adopt within its cache. Namespace and subfolder are optional (would + default to a `"default/"` subfolder) but library name is mandatory as we want every + downstream library to manage its own cache. + + Expected tree: + ```text + assets/ + └── datasets/ + │ ├── SQuAD/ + │ │ ├── downloaded/ + │ │ ├── extracted/ + │ │ └── processed/ + │ ├── Helsinki-NLP--tatoeba_mt/ + │ ├── downloaded/ + │ ├── extracted/ + │ └── processed/ + └── transformers/ + ├── default/ + │ ├── something/ + ├── bert-base-cased/ + │ ├── default/ + │ └── training/ + hub/ + └── models--julien-c--EsperBERTo-small/ + ├── blobs/ + │ ├── (...) + │ ├── (...) + ├── refs/ + │ └── (...) + └── [ 128] snapshots/ + ├── 2439f60ef33a0d46d85da5001d52aeda5b00ce9f/ + │ ├── (...) + └── bbc77c8132af1cc5cf678da3f1ddf2de43606d48/ + └── (...) + ``` + + + Args: + library_name (`str`): + Name of the library that will manage the cache folder. Example: `"dataset"`. + namespace (`str`, *optional*, defaults to "default"): + Namespace to which the data belongs. Example: `"SQuAD"`. + subfolder (`str`, *optional*, defaults to "default"): + Subfolder in which the data will be stored. Example: `extracted`. + assets_dir (`str`, `Path`, *optional*): + Path to the folder where assets are cached. This must not be the same folder + where Hub files are cached. Defaults to `HF_HOME / "assets"` if not provided. + Can also be set with `HF_ASSETS_CACHE` environment variable. + + Returns: + Path to the cache folder (`Path`). + + Example: + ```py + >>> from huggingface_hub import cached_assets_path + + >>> cached_assets_path(library_name="datasets", namespace="SQuAD", subfolder="download") + PosixPath('/home/wauplin/.cache/huggingface/extra/datasets/SQuAD/download') + + >>> cached_assets_path(library_name="datasets", namespace="SQuAD", subfolder="extracted") + PosixPath('/home/wauplin/.cache/huggingface/extra/datasets/SQuAD/extracted') + + >>> cached_assets_path(library_name="datasets", namespace="Helsinki-NLP/tatoeba_mt") + PosixPath('/home/wauplin/.cache/huggingface/extra/datasets/Helsinki-NLP--tatoeba_mt/default') + + >>> cached_assets_path(library_name="datasets", assets_dir="/tmp/tmp123456") + PosixPath('/tmp/tmp123456/datasets/default/default') + ``` + """ + # Resolve assets_dir + if assets_dir is None: + assets_dir = HF_ASSETS_CACHE + assets_dir = Path(assets_dir).expanduser().resolve() + + # Avoid names that could create path issues + for part in (" ", "/", "\\"): + library_name = library_name.replace(part, "--") + namespace = namespace.replace(part, "--") + subfolder = subfolder.replace(part, "--") + + # Path to subfolder is created + path = assets_dir / library_name / namespace / subfolder + try: + path.mkdir(exist_ok=True, parents=True) + except (FileExistsError, NotADirectoryError): + raise ValueError(f"Corrupted assets folder: cannot create directory because of an existing file ({path}).") + + # Return + return path diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/utils/_cache_manager.py b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_cache_manager.py new file mode 100644 index 0000000000000000000000000000000000000000..90d0e01f74812c5c3e65ba9313a155ee8e517927 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_cache_manager.py @@ -0,0 +1,866 @@ +# coding=utf-8 +# Copyright 2022-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains utilities to manage the HF cache directory.""" + +import os +import shutil +import time +from collections import defaultdict +from dataclasses import dataclass +from pathlib import Path +from typing import Dict, FrozenSet, List, Literal, Optional, Set, Union + +from huggingface_hub.errors import CacheNotFound, CorruptedCacheException + +from ..commands._cli_utils import tabulate +from ..constants import HF_HUB_CACHE +from . import logging + + +logger = logging.get_logger(__name__) + +REPO_TYPE_T = Literal["model", "dataset", "space"] + +# List of OS-created helper files that need to be ignored +FILES_TO_IGNORE = [".DS_Store"] + + +@dataclass(frozen=True) +class CachedFileInfo: + """Frozen data structure holding information about a single cached file. + + Args: + file_name (`str`): + Name of the file. Example: `config.json`. + file_path (`Path`): + Path of the file in the `snapshots` directory. The file path is a symlink + referring to a blob in the `blobs` folder. + blob_path (`Path`): + Path of the blob file. This is equivalent to `file_path.resolve()`. + size_on_disk (`int`): + Size of the blob file in bytes. + blob_last_accessed (`float`): + Timestamp of the last time the blob file has been accessed (from any + revision). + blob_last_modified (`float`): + Timestamp of the last time the blob file has been modified/created. + + > [!WARNING] + > `blob_last_accessed` and `blob_last_modified` reliability can depend on the OS you + > are using. See [python documentation](https://docs.python.org/3/library/os.html#os.stat_result) + > for more details. + """ + + file_name: str + file_path: Path + blob_path: Path + size_on_disk: int + + blob_last_accessed: float + blob_last_modified: float + + @property + def blob_last_accessed_str(self) -> str: + """ + (property) Timestamp of the last time the blob file has been accessed (from any + revision), returned as a human-readable string. + + Example: "2 weeks ago". + """ + return _format_timesince(self.blob_last_accessed) + + @property + def blob_last_modified_str(self) -> str: + """ + (property) Timestamp of the last time the blob file has been modified, returned + as a human-readable string. + + Example: "2 weeks ago". + """ + return _format_timesince(self.blob_last_modified) + + @property + def size_on_disk_str(self) -> str: + """ + (property) Size of the blob file as a human-readable string. + + Example: "42.2K". + """ + return _format_size(self.size_on_disk) + + +@dataclass(frozen=True) +class CachedRevisionInfo: + """Frozen data structure holding information about a revision. + + A revision correspond to a folder in the `snapshots` folder and is populated with + the exact tree structure as the repo on the Hub but contains only symlinks. A + revision can be either referenced by 1 or more `refs` or be "detached" (no refs). + + Args: + commit_hash (`str`): + Hash of the revision (unique). + Example: `"9338f7b671827df886678df2bdd7cc7b4f36dffd"`. + snapshot_path (`Path`): + Path to the revision directory in the `snapshots` folder. It contains the + exact tree structure as the repo on the Hub. + files: (`FrozenSet[CachedFileInfo]`): + Set of [`~CachedFileInfo`] describing all files contained in the snapshot. + refs (`FrozenSet[str]`): + Set of `refs` pointing to this revision. If the revision has no `refs`, it + is considered detached. + Example: `{"main", "2.4.0"}` or `{"refs/pr/1"}`. + size_on_disk (`int`): + Sum of the blob file sizes that are symlink-ed by the revision. + last_modified (`float`): + Timestamp of the last time the revision has been created/modified. + + > [!WARNING] + > `last_accessed` cannot be determined correctly on a single revision as blob files + > are shared across revisions. + + > [!WARNING] + > `size_on_disk` is not necessarily the sum of all file sizes because of possible + > duplicated files. Besides, only blobs are taken into account, not the (negligible) + > size of folders and symlinks. + """ + + commit_hash: str + snapshot_path: Path + size_on_disk: int + files: FrozenSet[CachedFileInfo] + refs: FrozenSet[str] + + last_modified: float + + @property + def last_modified_str(self) -> str: + """ + (property) Timestamp of the last time the revision has been modified, returned + as a human-readable string. + + Example: "2 weeks ago". + """ + return _format_timesince(self.last_modified) + + @property + def size_on_disk_str(self) -> str: + """ + (property) Sum of the blob file sizes as a human-readable string. + + Example: "42.2K". + """ + return _format_size(self.size_on_disk) + + @property + def nb_files(self) -> int: + """ + (property) Total number of files in the revision. + """ + return len(self.files) + + +@dataclass(frozen=True) +class CachedRepoInfo: + """Frozen data structure holding information about a cached repository. + + Args: + repo_id (`str`): + Repo id of the repo on the Hub. Example: `"google/fleurs"`. + repo_type (`Literal["dataset", "model", "space"]`): + Type of the cached repo. + repo_path (`Path`): + Local path to the cached repo. + size_on_disk (`int`): + Sum of the blob file sizes in the cached repo. + nb_files (`int`): + Total number of blob files in the cached repo. + revisions (`FrozenSet[CachedRevisionInfo]`): + Set of [`~CachedRevisionInfo`] describing all revisions cached in the repo. + last_accessed (`float`): + Timestamp of the last time a blob file of the repo has been accessed. + last_modified (`float`): + Timestamp of the last time a blob file of the repo has been modified/created. + + > [!WARNING] + > `size_on_disk` is not necessarily the sum of all revisions sizes because of + > duplicated files. Besides, only blobs are taken into account, not the (negligible) + > size of folders and symlinks. + + > [!WARNING] + > `last_accessed` and `last_modified` reliability can depend on the OS you are using. + > See [python documentation](https://docs.python.org/3/library/os.html#os.stat_result) + > for more details. + """ + + repo_id: str + repo_type: REPO_TYPE_T + repo_path: Path + size_on_disk: int + nb_files: int + revisions: FrozenSet[CachedRevisionInfo] + + last_accessed: float + last_modified: float + + @property + def last_accessed_str(self) -> str: + """ + (property) Last time a blob file of the repo has been accessed, returned as a + human-readable string. + + Example: "2 weeks ago". + """ + return _format_timesince(self.last_accessed) + + @property + def last_modified_str(self) -> str: + """ + (property) Last time a blob file of the repo has been modified, returned as a + human-readable string. + + Example: "2 weeks ago". + """ + return _format_timesince(self.last_modified) + + @property + def size_on_disk_str(self) -> str: + """ + (property) Sum of the blob file sizes as a human-readable string. + + Example: "42.2K". + """ + return _format_size(self.size_on_disk) + + @property + def refs(self) -> Dict[str, CachedRevisionInfo]: + """ + (property) Mapping between `refs` and revision data structures. + """ + return {ref: revision for revision in self.revisions for ref in revision.refs} + + +@dataclass(frozen=True) +class DeleteCacheStrategy: + """Frozen data structure holding the strategy to delete cached revisions. + + This object is not meant to be instantiated programmatically but to be returned by + [`~utils.HFCacheInfo.delete_revisions`]. See documentation for usage example. + + Args: + expected_freed_size (`float`): + Expected freed size once strategy is executed. + blobs (`FrozenSet[Path]`): + Set of blob file paths to be deleted. + refs (`FrozenSet[Path]`): + Set of reference file paths to be deleted. + repos (`FrozenSet[Path]`): + Set of entire repo paths to be deleted. + snapshots (`FrozenSet[Path]`): + Set of snapshots to be deleted (directory of symlinks). + """ + + expected_freed_size: int + blobs: FrozenSet[Path] + refs: FrozenSet[Path] + repos: FrozenSet[Path] + snapshots: FrozenSet[Path] + + @property + def expected_freed_size_str(self) -> str: + """ + (property) Expected size that will be freed as a human-readable string. + + Example: "42.2K". + """ + return _format_size(self.expected_freed_size) + + def execute(self) -> None: + """Execute the defined strategy. + + > [!WARNING] + > If this method is interrupted, the cache might get corrupted. Deletion order is + > implemented so that references and symlinks are deleted before the actual blob + > files. + + > [!WARNING] + > This method is irreversible. If executed, cached files are erased and must be + > downloaded again. + """ + # Deletion order matters. Blobs are deleted in last so that the user can't end + # up in a state where a `ref`` refers to a missing snapshot or a snapshot + # symlink refers to a deleted blob. + + # Delete entire repos + for path in self.repos: + _try_delete_path(path, path_type="repo") + + # Delete snapshot directories + for path in self.snapshots: + _try_delete_path(path, path_type="snapshot") + + # Delete refs files + for path in self.refs: + _try_delete_path(path, path_type="ref") + + # Delete blob files + for path in self.blobs: + _try_delete_path(path, path_type="blob") + + logger.info(f"Cache deletion done. Saved {self.expected_freed_size_str}.") + + +@dataclass(frozen=True) +class HFCacheInfo: + """Frozen data structure holding information about the entire cache-system. + + This data structure is returned by [`scan_cache_dir`] and is immutable. + + Args: + size_on_disk (`int`): + Sum of all valid repo sizes in the cache-system. + repos (`FrozenSet[CachedRepoInfo]`): + Set of [`~CachedRepoInfo`] describing all valid cached repos found on the + cache-system while scanning. + warnings (`List[CorruptedCacheException]`): + List of [`~CorruptedCacheException`] that occurred while scanning the cache. + Those exceptions are captured so that the scan can continue. Corrupted repos + are skipped from the scan. + + > [!WARNING] + > Here `size_on_disk` is equal to the sum of all repo sizes (only blobs). However if + > some cached repos are corrupted, their sizes are not taken into account. + """ + + size_on_disk: int + repos: FrozenSet[CachedRepoInfo] + warnings: List[CorruptedCacheException] + + @property + def size_on_disk_str(self) -> str: + """ + (property) Sum of all valid repo sizes in the cache-system as a human-readable + string. + + Example: "42.2K". + """ + return _format_size(self.size_on_disk) + + def delete_revisions(self, *revisions: str) -> DeleteCacheStrategy: + """Prepare the strategy to delete one or more revisions cached locally. + + Input revisions can be any revision hash. If a revision hash is not found in the + local cache, a warning is thrown but no error is raised. Revisions can be from + different cached repos since hashes are unique across repos, + + Examples: + ```py + >>> from huggingface_hub import scan_cache_dir + >>> cache_info = scan_cache_dir() + >>> delete_strategy = cache_info.delete_revisions( + ... "81fd1d6e7847c99f5862c9fb81387956d99ec7aa" + ... ) + >>> print(f"Will free {delete_strategy.expected_freed_size_str}.") + Will free 7.9K. + >>> delete_strategy.execute() + Cache deletion done. Saved 7.9K. + ``` + + ```py + >>> from huggingface_hub import scan_cache_dir + >>> scan_cache_dir().delete_revisions( + ... "81fd1d6e7847c99f5862c9fb81387956d99ec7aa", + ... "e2983b237dccf3ab4937c97fa717319a9ca1a96d", + ... "6c0e6080953db56375760c0471a8c5f2929baf11", + ... ).execute() + Cache deletion done. Saved 8.6G. + ``` + + > [!WARNING] + > `delete_revisions` returns a [`~utils.DeleteCacheStrategy`] object that needs to + > be executed. The [`~utils.DeleteCacheStrategy`] is not meant to be modified but + > allows having a dry run before actually executing the deletion. + """ + hashes_to_delete: Set[str] = set(revisions) + + repos_with_revisions: Dict[CachedRepoInfo, Set[CachedRevisionInfo]] = defaultdict(set) + + for repo in self.repos: + for revision in repo.revisions: + if revision.commit_hash in hashes_to_delete: + repos_with_revisions[repo].add(revision) + hashes_to_delete.remove(revision.commit_hash) + + if len(hashes_to_delete) > 0: + logger.warning(f"Revision(s) not found - cannot delete them: {', '.join(hashes_to_delete)}") + + delete_strategy_blobs: Set[Path] = set() + delete_strategy_refs: Set[Path] = set() + delete_strategy_repos: Set[Path] = set() + delete_strategy_snapshots: Set[Path] = set() + delete_strategy_expected_freed_size = 0 + + for affected_repo, revisions_to_delete in repos_with_revisions.items(): + other_revisions = affected_repo.revisions - revisions_to_delete + + # If no other revisions, it means all revisions are deleted + # -> delete the entire cached repo + if len(other_revisions) == 0: + delete_strategy_repos.add(affected_repo.repo_path) + delete_strategy_expected_freed_size += affected_repo.size_on_disk + continue + + # Some revisions of the repo will be deleted but not all. We need to filter + # which blob files will not be linked anymore. + for revision_to_delete in revisions_to_delete: + # Snapshot dir + delete_strategy_snapshots.add(revision_to_delete.snapshot_path) + + # Refs dir + for ref in revision_to_delete.refs: + delete_strategy_refs.add(affected_repo.repo_path / "refs" / ref) + + # Blobs dir + for file in revision_to_delete.files: + if file.blob_path not in delete_strategy_blobs: + is_file_alone = True + for revision in other_revisions: + for rev_file in revision.files: + if file.blob_path == rev_file.blob_path: + is_file_alone = False + break + if not is_file_alone: + break + + # Blob file not referenced by remaining revisions -> delete + if is_file_alone: + delete_strategy_blobs.add(file.blob_path) + delete_strategy_expected_freed_size += file.size_on_disk + + # Return the strategy instead of executing it. + return DeleteCacheStrategy( + blobs=frozenset(delete_strategy_blobs), + refs=frozenset(delete_strategy_refs), + repos=frozenset(delete_strategy_repos), + snapshots=frozenset(delete_strategy_snapshots), + expected_freed_size=delete_strategy_expected_freed_size, + ) + + def export_as_table(self, *, verbosity: int = 0) -> str: + """Generate a table from the [`HFCacheInfo`] object. + + Pass `verbosity=0` to get a table with a single row per repo, with columns + "repo_id", "repo_type", "size_on_disk", "nb_files", "last_accessed", "last_modified", "refs", "local_path". + + Pass `verbosity=1` to get a table with a row per repo and revision (thus multiple rows can appear for a single repo), with columns + "repo_id", "repo_type", "revision", "size_on_disk", "nb_files", "last_modified", "refs", "local_path". + + Example: + ```py + >>> from huggingface_hub.utils import scan_cache_dir + + >>> hf_cache_info = scan_cache_dir() + HFCacheInfo(...) + + >>> print(hf_cache_info.export_as_table()) + REPO ID REPO TYPE SIZE ON DISK NB FILES LAST_ACCESSED LAST_MODIFIED REFS LOCAL PATH + --------------------------------------------------- --------- ------------ -------- ------------- ------------- ---- -------------------------------------------------------------------------------------------------- + roberta-base model 2.7M 5 1 day ago 1 week ago main ~/.cache/huggingface/hub/models--roberta-base + suno/bark model 8.8K 1 1 week ago 1 week ago main ~/.cache/huggingface/hub/models--suno--bark + t5-base model 893.8M 4 4 days ago 7 months ago main ~/.cache/huggingface/hub/models--t5-base + t5-large model 3.0G 4 5 weeks ago 5 months ago main ~/.cache/huggingface/hub/models--t5-large + + >>> print(hf_cache_info.export_as_table(verbosity=1)) + REPO ID REPO TYPE REVISION SIZE ON DISK NB FILES LAST_MODIFIED REFS LOCAL PATH + --------------------------------------------------- --------- ---------------------------------------- ------------ -------- ------------- ---- ----------------------------------------------------------------------------------------------------------------------------------------------------- + roberta-base model e2da8e2f811d1448a5b465c236feacd80ffbac7b 2.7M 5 1 week ago main ~/.cache/huggingface/hub/models--roberta-base/snapshots/e2da8e2f811d1448a5b465c236feacd80ffbac7b + suno/bark model 70a8a7d34168586dc5d028fa9666aceade177992 8.8K 1 1 week ago main ~/.cache/huggingface/hub/models--suno--bark/snapshots/70a8a7d34168586dc5d028fa9666aceade177992 + t5-base model a9723ea7f1b39c1eae772870f3b547bf6ef7e6c1 893.8M 4 7 months ago main ~/.cache/huggingface/hub/models--t5-base/snapshots/a9723ea7f1b39c1eae772870f3b547bf6ef7e6c1 + t5-large model 150ebc2c4b72291e770f58e6057481c8d2ed331a 3.0G 4 5 months ago main ~/.cache/huggingface/hub/models--t5-large/snapshots/150ebc2c4b72291e770f58e6057481c8d2ed331a + ``` + + Args: + verbosity (`int`, *optional*): + The verbosity level. Defaults to 0. + + Returns: + `str`: The table as a string. + """ + if verbosity == 0: + return tabulate( + rows=[ + [ + repo.repo_id, + repo.repo_type, + "{:>12}".format(repo.size_on_disk_str), + repo.nb_files, + repo.last_accessed_str, + repo.last_modified_str, + ", ".join(sorted(repo.refs)), + str(repo.repo_path), + ] + for repo in sorted(self.repos, key=lambda repo: repo.repo_path) + ], + headers=[ + "REPO ID", + "REPO TYPE", + "SIZE ON DISK", + "NB FILES", + "LAST_ACCESSED", + "LAST_MODIFIED", + "REFS", + "LOCAL PATH", + ], + ) + else: + return tabulate( + rows=[ + [ + repo.repo_id, + repo.repo_type, + revision.commit_hash, + "{:>12}".format(revision.size_on_disk_str), + revision.nb_files, + revision.last_modified_str, + ", ".join(sorted(revision.refs)), + str(revision.snapshot_path), + ] + for repo in sorted(self.repos, key=lambda repo: repo.repo_path) + for revision in sorted(repo.revisions, key=lambda revision: revision.commit_hash) + ], + headers=[ + "REPO ID", + "REPO TYPE", + "REVISION", + "SIZE ON DISK", + "NB FILES", + "LAST_MODIFIED", + "REFS", + "LOCAL PATH", + ], + ) + + +def scan_cache_dir(cache_dir: Optional[Union[str, Path]] = None) -> HFCacheInfo: + """Scan the entire HF cache-system and return a [`~HFCacheInfo`] structure. + + Use `scan_cache_dir` in order to programmatically scan your cache-system. The cache + will be scanned repo by repo. If a repo is corrupted, a [`~CorruptedCacheException`] + will be thrown internally but captured and returned in the [`~HFCacheInfo`] + structure. Only valid repos get a proper report. + + ```py + >>> from huggingface_hub import scan_cache_dir + + >>> hf_cache_info = scan_cache_dir() + HFCacheInfo( + size_on_disk=3398085269, + repos=frozenset({ + CachedRepoInfo( + repo_id='t5-small', + repo_type='model', + repo_path=PosixPath(...), + size_on_disk=970726914, + nb_files=11, + revisions=frozenset({ + CachedRevisionInfo( + commit_hash='d78aea13fa7ecd06c29e3e46195d6341255065d5', + size_on_disk=970726339, + snapshot_path=PosixPath(...), + files=frozenset({ + CachedFileInfo( + file_name='config.json', + size_on_disk=1197 + file_path=PosixPath(...), + blob_path=PosixPath(...), + ), + CachedFileInfo(...), + ... + }), + ), + CachedRevisionInfo(...), + ... + }), + ), + CachedRepoInfo(...), + ... + }), + warnings=[ + CorruptedCacheException("Snapshots dir doesn't exist in cached repo: ..."), + CorruptedCacheException(...), + ... + ], + ) + ``` + + You can also print a detailed report directly from the `hf` command line using: + ```text + > hf cache scan + REPO ID REPO TYPE SIZE ON DISK NB FILES REFS LOCAL PATH + --------------------------- --------- ------------ -------- ------------------- ------------------------------------------------------------------------- + glue dataset 116.3K 15 1.17.0, main, 2.4.0 /Users/lucain/.cache/huggingface/hub/datasets--glue + google/fleurs dataset 64.9M 6 main, refs/pr/1 /Users/lucain/.cache/huggingface/hub/datasets--google--fleurs + Jean-Baptiste/camembert-ner model 441.0M 7 main /Users/lucain/.cache/huggingface/hub/models--Jean-Baptiste--camembert-ner + bert-base-cased model 1.9G 13 main /Users/lucain/.cache/huggingface/hub/models--bert-base-cased + t5-base model 10.1K 3 main /Users/lucain/.cache/huggingface/hub/models--t5-base + t5-small model 970.7M 11 refs/pr/1, main /Users/lucain/.cache/huggingface/hub/models--t5-small + + Done in 0.0s. Scanned 6 repo(s) for a total of 3.4G. + Got 1 warning(s) while scanning. Use -vvv to print details. + ``` + + Args: + cache_dir (`str` or `Path`, `optional`): + Cache directory to cache. Defaults to the default HF cache directory. + + > [!WARNING] + > Raises: + > + > `CacheNotFound` + > If the cache directory does not exist. + > + > [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + > If the cache directory is a file, instead of a directory. + + Returns: a [`~HFCacheInfo`] object. + """ + if cache_dir is None: + cache_dir = HF_HUB_CACHE + + cache_dir = Path(cache_dir).expanduser().resolve() + if not cache_dir.exists(): + raise CacheNotFound( + f"Cache directory not found: {cache_dir}. Please use `cache_dir` argument or set `HF_HUB_CACHE` environment variable.", + cache_dir=cache_dir, + ) + + if cache_dir.is_file(): + raise ValueError( + f"Scan cache expects a directory but found a file: {cache_dir}. Please use `cache_dir` argument or set `HF_HUB_CACHE` environment variable." + ) + + repos: Set[CachedRepoInfo] = set() + warnings: List[CorruptedCacheException] = [] + for repo_path in cache_dir.iterdir(): + if repo_path.name == ".locks": # skip './.locks/' folder + continue + try: + repos.add(_scan_cached_repo(repo_path)) + except CorruptedCacheException as e: + warnings.append(e) + + return HFCacheInfo( + repos=frozenset(repos), + size_on_disk=sum(repo.size_on_disk for repo in repos), + warnings=warnings, + ) + + +def _scan_cached_repo(repo_path: Path) -> CachedRepoInfo: + """Scan a single cache repo and return information about it. + + Any unexpected behavior will raise a [`~CorruptedCacheException`]. + """ + if not repo_path.is_dir(): + raise CorruptedCacheException(f"Repo path is not a directory: {repo_path}") + + if "--" not in repo_path.name: + raise CorruptedCacheException(f"Repo path is not a valid HuggingFace cache directory: {repo_path}") + + repo_type, repo_id = repo_path.name.split("--", maxsplit=1) + repo_type = repo_type[:-1] # "models" -> "model" + repo_id = repo_id.replace("--", "/") # google/fleurs -> "google/fleurs" + + if repo_type not in {"dataset", "model", "space"}: + raise CorruptedCacheException( + f"Repo type must be `dataset`, `model` or `space`, found `{repo_type}` ({repo_path})." + ) + + blob_stats: Dict[Path, os.stat_result] = {} # Key is blob_path, value is blob stats + + snapshots_path = repo_path / "snapshots" + refs_path = repo_path / "refs" + + if not snapshots_path.exists() or not snapshots_path.is_dir(): + raise CorruptedCacheException(f"Snapshots dir doesn't exist in cached repo: {snapshots_path}") + + # Scan over `refs` directory + + # key is revision hash, value is set of refs + refs_by_hash: Dict[str, Set[str]] = defaultdict(set) + if refs_path.exists(): + # Example of `refs` directory + # ── refs + # ├── main + # └── refs + # └── pr + # └── 1 + if refs_path.is_file(): + raise CorruptedCacheException(f"Refs directory cannot be a file: {refs_path}") + + for ref_path in refs_path.glob("**/*"): + # glob("**/*") iterates over all files and directories -> skip directories + if ref_path.is_dir() or ref_path.name in FILES_TO_IGNORE: + continue + + ref_name = str(ref_path.relative_to(refs_path)) + with ref_path.open() as f: + commit_hash = f.read() + + refs_by_hash[commit_hash].add(ref_name) + + # Scan snapshots directory + cached_revisions: Set[CachedRevisionInfo] = set() + for revision_path in snapshots_path.iterdir(): + # Ignore OS-created helper files + if revision_path.name in FILES_TO_IGNORE: + continue + if revision_path.is_file(): + raise CorruptedCacheException(f"Snapshots folder corrupted. Found a file: {revision_path}") + + cached_files = set() + for file_path in revision_path.glob("**/*"): + # glob("**/*") iterates over all files and directories -> skip directories + if file_path.is_dir(): + continue + + blob_path = Path(file_path).resolve() + if not blob_path.exists(): + raise CorruptedCacheException(f"Blob missing (broken symlink): {blob_path}") + + if blob_path not in blob_stats: + blob_stats[blob_path] = blob_path.stat() + + cached_files.add( + CachedFileInfo( + file_name=file_path.name, + file_path=file_path, + size_on_disk=blob_stats[blob_path].st_size, + blob_path=blob_path, + blob_last_accessed=blob_stats[blob_path].st_atime, + blob_last_modified=blob_stats[blob_path].st_mtime, + ) + ) + + # Last modified is either the last modified blob file or the revision folder + # itself if it is empty + if len(cached_files) > 0: + revision_last_modified = max(blob_stats[file.blob_path].st_mtime for file in cached_files) + else: + revision_last_modified = revision_path.stat().st_mtime + + cached_revisions.add( + CachedRevisionInfo( + commit_hash=revision_path.name, + files=frozenset(cached_files), + refs=frozenset(refs_by_hash.pop(revision_path.name, set())), + size_on_disk=sum( + blob_stats[blob_path].st_size for blob_path in set(file.blob_path for file in cached_files) + ), + snapshot_path=revision_path, + last_modified=revision_last_modified, + ) + ) + + # Check that all refs referred to an existing revision + if len(refs_by_hash) > 0: + raise CorruptedCacheException( + f"Reference(s) refer to missing commit hashes: {dict(refs_by_hash)} ({repo_path})." + ) + + # Last modified is either the last modified blob file or the repo folder itself if + # no blob files has been found. Same for last accessed. + if len(blob_stats) > 0: + repo_last_accessed = max(stat.st_atime for stat in blob_stats.values()) + repo_last_modified = max(stat.st_mtime for stat in blob_stats.values()) + else: + repo_stats = repo_path.stat() + repo_last_accessed = repo_stats.st_atime + repo_last_modified = repo_stats.st_mtime + + # Build and return frozen structure + return CachedRepoInfo( + nb_files=len(blob_stats), + repo_id=repo_id, + repo_path=repo_path, + repo_type=repo_type, # type: ignore + revisions=frozenset(cached_revisions), + size_on_disk=sum(stat.st_size for stat in blob_stats.values()), + last_accessed=repo_last_accessed, + last_modified=repo_last_modified, + ) + + +def _format_size(num: int) -> str: + """Format size in bytes into a human-readable string. + + Taken from https://stackoverflow.com/a/1094933 + """ + num_f = float(num) + for unit in ["", "K", "M", "G", "T", "P", "E", "Z"]: + if abs(num_f) < 1000.0: + return f"{num_f:3.1f}{unit}" + num_f /= 1000.0 + return f"{num_f:.1f}Y" + + +_TIMESINCE_CHUNKS = ( + # Label, divider, max value + ("second", 1, 60), + ("minute", 60, 60), + ("hour", 60 * 60, 24), + ("day", 60 * 60 * 24, 6), + ("week", 60 * 60 * 24 * 7, 6), + ("month", 60 * 60 * 24 * 30, 11), + ("year", 60 * 60 * 24 * 365, None), +) + + +def _format_timesince(ts: float) -> str: + """Format timestamp in seconds into a human-readable string, relative to now. + + Vaguely inspired by Django's `timesince` formatter. + """ + delta = time.time() - ts + if delta < 20: + return "a few seconds ago" + for label, divider, max_value in _TIMESINCE_CHUNKS: # noqa: B007 + value = round(delta / divider) + if max_value is not None and value <= max_value: + break + return f"{value} {label}{'s' if value > 1 else ''} ago" + + +def _try_delete_path(path: Path, path_type: str) -> None: + """Try to delete a local file or folder. + + If the path does not exists, error is logged as a warning and then ignored. + + Args: + path (`Path`) + Path to delete. Can be a file or a folder. + path_type (`str`) + What path are we deleting ? Only for logging purposes. Example: "snapshot". + """ + logger.info(f"Delete {path_type}: {path}") + try: + if path.is_file(): + os.remove(path) + else: + shutil.rmtree(path) + except FileNotFoundError: + logger.warning(f"Couldn't delete {path_type}: file not found ({path})", exc_info=True) + except PermissionError: + logger.warning(f"Couldn't delete {path_type}: permission denied ({path})", exc_info=True) diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/utils/_chunk_utils.py b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_chunk_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..fe8ecc9c94f9c09503761e734a005124d3291a52 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_chunk_utils.py @@ -0,0 +1,64 @@ +# coding=utf-8 +# Copyright 2022-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains a utility to iterate by chunks over an iterator.""" + +import itertools +from typing import Iterable, TypeVar + + +T = TypeVar("T") + + +def chunk_iterable(iterable: Iterable[T], chunk_size: int) -> Iterable[Iterable[T]]: + """Iterates over an iterator chunk by chunk. + + Taken from https://stackoverflow.com/a/8998040. + See also https://github.com/huggingface/huggingface_hub/pull/920#discussion_r938793088. + + Args: + iterable (`Iterable`): + The iterable on which we want to iterate. + chunk_size (`int`): + Size of the chunks. Must be a strictly positive integer (e.g. >0). + + Example: + + ```python + >>> from huggingface_hub.utils import chunk_iterable + + >>> for items in chunk_iterable(range(17), chunk_size=8): + ... print(items) + # [0, 1, 2, 3, 4, 5, 6, 7] + # [8, 9, 10, 11, 12, 13, 14, 15] + # [16] # smaller last chunk + ``` + + Raises: + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If `chunk_size` <= 0. + + > [!WARNING] + > The last chunk can be smaller than `chunk_size`. + """ + if not isinstance(chunk_size, int) or chunk_size <= 0: + raise ValueError("`chunk_size` must be a strictly positive integer (>0).") + + iterator = iter(iterable) + while True: + try: + next_item = next(iterator) + except StopIteration: + return + yield itertools.chain((next_item,), itertools.islice(iterator, chunk_size - 1)) diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/utils/_datetime.py b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_datetime.py new file mode 100644 index 0000000000000000000000000000000000000000..1a7f44285d1c826006c97176ca66c3e9c33f61c0 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_datetime.py @@ -0,0 +1,67 @@ +# coding=utf-8 +# Copyright 2022-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains utilities to handle datetimes in Huggingface Hub.""" + +from datetime import datetime, timezone + + +def parse_datetime(date_string: str) -> datetime: + """ + Parses a date_string returned from the server to a datetime object. + + This parser is a weak-parser is the sense that it handles only a single format of + date_string. It is expected that the server format will never change. The + implementation depends only on the standard lib to avoid an external dependency + (python-dateutil). See full discussion about this decision on PR: + https://github.com/huggingface/huggingface_hub/pull/999. + + Example: + ```py + > parse_datetime('2022-08-19T07:19:38.123Z') + datetime.datetime(2022, 8, 19, 7, 19, 38, 123000, tzinfo=timezone.utc) + ``` + + Args: + date_string (`str`): + A string representing a datetime returned by the Hub server. + String is expected to follow '%Y-%m-%dT%H:%M:%S.%fZ' pattern. + + Returns: + A python datetime object. + + Raises: + :class:`ValueError`: + If `date_string` cannot be parsed. + """ + try: + # Normalize the string to always have 6 digits of fractional seconds + if date_string.endswith("Z"): + # Case 1: No decimal point (e.g., "2024-11-16T00:27:02Z") + if "." not in date_string: + # No fractional seconds - insert .000000 + date_string = date_string[:-1] + ".000000Z" + # Case 2: Has decimal point (e.g., "2022-08-19T07:19:38.123456789Z") + else: + # Get the fractional and base parts + base, fraction = date_string[:-1].split(".") + # fraction[:6] takes first 6 digits and :0<6 pads with zeros if less than 6 digits + date_string = f"{base}.{fraction[:6]:0<6}Z" + + return datetime.strptime(date_string, "%Y-%m-%dT%H:%M:%S.%fZ").replace(tzinfo=timezone.utc) + except ValueError as e: + raise ValueError( + f"Cannot parse '{date_string}' as a datetime. Date string is expected to" + " follow '%Y-%m-%dT%H:%M:%S.%fZ' pattern." + ) from e diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/utils/_deprecation.py b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_deprecation.py new file mode 100644 index 0000000000000000000000000000000000000000..4cb8d6e418c76accd1ecd61158b4bdd265e12f71 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_deprecation.py @@ -0,0 +1,136 @@ +import warnings +from functools import wraps +from inspect import Parameter, signature +from typing import Iterable, Optional + + +def _deprecate_positional_args(*, version: str): + """Decorator for methods that issues warnings for positional arguments. + Using the keyword-only argument syntax in pep 3102, arguments after the + * will issue a warning when passed as a positional argument. + + Args: + version (`str`): + The version when positional arguments will result in error. + """ + + def _inner_deprecate_positional_args(f): + sig = signature(f) + kwonly_args = [] + all_args = [] + for name, param in sig.parameters.items(): + if param.kind == Parameter.POSITIONAL_OR_KEYWORD: + all_args.append(name) + elif param.kind == Parameter.KEYWORD_ONLY: + kwonly_args.append(name) + + @wraps(f) + def inner_f(*args, **kwargs): + extra_args = len(args) - len(all_args) + if extra_args <= 0: + return f(*args, **kwargs) + # extra_args > 0 + args_msg = [ + f"{name}='{arg}'" if isinstance(arg, str) else f"{name}={arg}" + for name, arg in zip(kwonly_args[:extra_args], args[-extra_args:]) + ] + args_msg = ", ".join(args_msg) + warnings.warn( + f"Deprecated positional argument(s) used in '{f.__name__}': pass" + f" {args_msg} as keyword args. From version {version} passing these" + " as positional arguments will result in an error,", + FutureWarning, + ) + kwargs.update(zip(sig.parameters, args)) + return f(**kwargs) + + return inner_f + + return _inner_deprecate_positional_args + + +def _deprecate_arguments( + *, + version: str, + deprecated_args: Iterable[str], + custom_message: Optional[str] = None, +): + """Decorator to issue warnings when using deprecated arguments. + + TODO: could be useful to be able to set a custom error message. + + Args: + version (`str`): + The version when deprecated arguments will result in error. + deprecated_args (`List[str]`): + List of the arguments to be deprecated. + custom_message (`str`, *optional*): + Warning message that is raised. If not passed, a default warning message + will be created. + """ + + def _inner_deprecate_positional_args(f): + sig = signature(f) + + @wraps(f) + def inner_f(*args, **kwargs): + # Check for used deprecated arguments + used_deprecated_args = [] + for _, parameter in zip(args, sig.parameters.values()): + if parameter.name in deprecated_args: + used_deprecated_args.append(parameter.name) + for kwarg_name, kwarg_value in kwargs.items(): + if ( + # If argument is deprecated but still used + kwarg_name in deprecated_args + # And then the value is not the default value + and kwarg_value != sig.parameters[kwarg_name].default + ): + used_deprecated_args.append(kwarg_name) + + # Warn and proceed + if len(used_deprecated_args) > 0: + message = ( + f"Deprecated argument(s) used in '{f.__name__}':" + f" {', '.join(used_deprecated_args)}. Will not be supported from" + f" version '{version}'." + ) + if custom_message is not None: + message += "\n\n" + custom_message + warnings.warn(message, FutureWarning) + return f(*args, **kwargs) + + return inner_f + + return _inner_deprecate_positional_args + + +def _deprecate_method(*, version: str, message: Optional[str] = None): + """Decorator to issue warnings when using a deprecated method. + + Args: + version (`str`): + The version when deprecated arguments will result in error. + message (`str`, *optional*): + Warning message that is raised. If not passed, a default warning message + will be created. + """ + + def _inner_deprecate_method(f): + name = f.__name__ + if name == "__init__": + name = f.__qualname__.split(".")[0] # class name instead of method name + + @wraps(f) + def inner_f(*args, **kwargs): + warning_message = ( + f"'{name}' (from '{f.__module__}') is deprecated and will be removed from version '{version}'." + ) + if message is not None: + warning_message += " " + message + warnings.warn(warning_message, FutureWarning) + return f(*args, **kwargs) + + return inner_f + + return _inner_deprecate_method diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/utils/_dotenv.py b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_dotenv.py new file mode 100644 index 0000000000000000000000000000000000000000..23b8a1b70a4827fc8ae4149c2b1b1e4b00ed7ca2 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_dotenv.py @@ -0,0 +1,55 @@ +# AI-generated module (ChatGPT) +import re +from typing import Dict, Optional + + +def load_dotenv(dotenv_str: str, environ: Optional[Dict[str, str]] = None) -> Dict[str, str]: + """ + Parse a DOTENV-format string and return a dictionary of key-value pairs. + Handles quoted values, comments, export keyword, and blank lines. + """ + env: Dict[str, str] = {} + line_pattern = re.compile( + r""" + ^\s* + (?:export[^\S\n]+)? # optional export + ([A-Za-z_][A-Za-z0-9_]*) # key + [^\S\n]*(=)?[^\S\n]* + ( # value group + (?: + '(?:\\'|[^'])*' # single-quoted value + | \"(?:\\\"|[^\"])*\" # double-quoted value + | [^#\n\r]+? # unquoted value + ) + )? + [^\S\n]*(?:\#.*)?$ # optional inline comment + """, + re.VERBOSE, + ) + + for line in dotenv_str.splitlines(): + line = line.strip() + if not line or line.startswith("#"): + continue # Skip comments and empty lines + + match = line_pattern.match(line) + if match: + key = match.group(1) + val = None + if match.group(2): # if there is '=' + raw_val = match.group(3) or "" + val = raw_val.strip() + # Remove surrounding quotes if quoted + if (val.startswith('"') and val.endswith('"')) or (val.startswith("'") and val.endswith("'")): + val = val[1:-1] + val = val.replace(r"\n", "\n").replace(r"\t", "\t").replace(r"\"", '"').replace(r"\\", "\\") + if raw_val.startswith('"'): + val = val.replace(r"\$", "$") # only in double quotes + elif environ is not None: + # Get it from the current environment + val = environ.get(key) + + if val is not None: + env[key] = val + + return env diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/utils/_experimental.py b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_experimental.py new file mode 100644 index 0000000000000000000000000000000000000000..40b0ed90ff8af6797758d59b93019498cd72f9ad --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_experimental.py @@ -0,0 +1,68 @@ +# coding=utf-8 +# Copyright 2023-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains utilities to flag a feature as "experimental" in Huggingface Hub.""" + +import warnings +from functools import wraps +from typing import Callable + +from .. import constants + + +def experimental(fn: Callable) -> Callable: + """Decorator to flag a feature as experimental. + + An experimental feature triggers a warning when used as it might be subject to breaking changes without prior notice + in the future. + + Warnings can be disabled by setting `HF_HUB_DISABLE_EXPERIMENTAL_WARNING=1` as environment variable. + + Args: + fn (`Callable`): + The function to flag as experimental. + + Returns: + `Callable`: The decorated function. + + Example: + + ```python + >>> from huggingface_hub.utils import experimental + + >>> @experimental + ... def my_function(): + ... print("Hello world!") + + >>> my_function() + UserWarning: 'my_function' is experimental and might be subject to breaking changes in the future without prior + notice. You can disable this warning by setting `HF_HUB_DISABLE_EXPERIMENTAL_WARNING=1` as environment variable. + Hello world! + ``` + """ + # For classes, put the "experimental" around the "__new__" method => __new__ will be removed in warning message + name = fn.__qualname__[: -len(".__new__")] if fn.__qualname__.endswith(".__new__") else fn.__qualname__ + + @wraps(fn) + def _inner_fn(*args, **kwargs): + if not constants.HF_HUB_DISABLE_EXPERIMENTAL_WARNING: + warnings.warn( + f"'{name}' is experimental and might be subject to breaking changes in the future without prior notice." + " You can disable this warning by setting `HF_HUB_DISABLE_EXPERIMENTAL_WARNING=1` as environment" + " variable.", + UserWarning, + ) + return fn(*args, **kwargs) + + return _inner_fn diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/utils/_fixes.py b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_fixes.py new file mode 100644 index 0000000000000000000000000000000000000000..560003b6222058b03791491b1ce70ea9d7a94404 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_fixes.py @@ -0,0 +1,133 @@ +# JSONDecodeError was introduced in requests=2.27 released in 2022. +# This allows us to support older requests for users +# More information: https://github.com/psf/requests/pull/5856 +try: + from requests import JSONDecodeError # type: ignore # noqa: F401 +except ImportError: + try: + from simplejson import JSONDecodeError # type: ignore # noqa: F401 + except ImportError: + from json import JSONDecodeError # type: ignore # noqa: F401 +import contextlib +import os +import shutil +import stat +import tempfile +import time +from functools import partial +from pathlib import Path +from typing import Callable, Generator, Optional, Union + +import yaml +from filelock import BaseFileLock, FileLock, SoftFileLock, Timeout + +from .. import constants +from . import logging + + +logger = logging.get_logger(__name__) + +# Wrap `yaml.dump` to set `allow_unicode=True` by default. +# +# Example: +# ```py +# >>> yaml.dump({"emoji": "👀", "some unicode": "日本か"}) +# 'emoji: "\\U0001F440"\nsome unicode: "\\u65E5\\u672C\\u304B"\n' +# +# >>> yaml_dump({"emoji": "👀", "some unicode": "日本か"}) +# 'emoji: "👀"\nsome unicode: "日本か"\n' +# ``` +yaml_dump: Callable[..., str] = partial(yaml.dump, stream=None, allow_unicode=True) # type: ignore + + +@contextlib.contextmanager +def SoftTemporaryDirectory( + suffix: Optional[str] = None, + prefix: Optional[str] = None, + dir: Optional[Union[Path, str]] = None, + **kwargs, +) -> Generator[Path, None, None]: + """ + Context manager to create a temporary directory and safely delete it. + + If tmp directory cannot be deleted normally, we set the WRITE permission and retry. + If cleanup still fails, we give up but don't raise an exception. This is equivalent + to `tempfile.TemporaryDirectory(..., ignore_cleanup_errors=True)` introduced in + Python 3.10. + + See https://www.scivision.dev/python-tempfile-permission-error-windows/. + """ + tmpdir = tempfile.TemporaryDirectory(prefix=prefix, suffix=suffix, dir=dir, **kwargs) + yield Path(tmpdir.name).resolve() + + try: + # First once with normal cleanup + shutil.rmtree(tmpdir.name) + except Exception: + # If failed, try to set write permission and retry + try: + shutil.rmtree(tmpdir.name, onerror=_set_write_permission_and_retry) + except Exception: + pass + + # And finally, cleanup the tmpdir. + # If it fails again, give up but do not throw error + try: + tmpdir.cleanup() + except Exception: + pass + + +def _set_write_permission_and_retry(func, path, excinfo): + os.chmod(path, stat.S_IWRITE) + func(path) + + +@contextlib.contextmanager +def WeakFileLock( + lock_file: Union[str, Path], *, timeout: Optional[float] = None +) -> Generator[BaseFileLock, None, None]: + """A filelock with some custom logic. + + This filelock is weaker than the default filelock in that: + 1. It won't raise an exception if release fails. + 2. It will default to a SoftFileLock if the filesystem does not support flock. + + An INFO log message is emitted every 10 seconds if the lock is not acquired immediately. + If a timeout is provided, a `filelock.Timeout` exception is raised if the lock is not acquired within the timeout. + """ + log_interval = constants.FILELOCK_LOG_EVERY_SECONDS + lock = FileLock(lock_file, timeout=log_interval) + start_time = time.time() + + while True: + elapsed_time = time.time() - start_time + if timeout is not None and elapsed_time >= timeout: + raise Timeout(str(lock_file)) + + try: + lock.acquire(timeout=min(log_interval, timeout - elapsed_time) if timeout else log_interval) + except Timeout: + logger.info( + f"Still waiting to acquire lock on {lock_file} (elapsed: {time.time() - start_time:.1f} seconds)" + ) + except NotImplementedError as e: + if "use SoftFileLock instead" in str(e): + logger.warning( + "FileSystem does not appear to support flock. Falling back to SoftFileLock for %s", lock_file + ) + lock = SoftFileLock(lock_file, timeout=log_interval) + continue + else: + break + + try: + yield lock + finally: + try: + lock.release() + except OSError: + try: + Path(lock_file).unlink() + except OSError: + pass diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/utils/_git_credential.py b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_git_credential.py new file mode 100644 index 0000000000000000000000000000000000000000..5ad84648a0093de6e6defc178e4ffffe985f50e4 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_git_credential.py @@ -0,0 +1,121 @@ +# coding=utf-8 +# Copyright 2022-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains utilities to manage Git credentials.""" + +import re +import subprocess +from typing import List, Optional + +from ..constants import ENDPOINT +from ._subprocess import run_interactive_subprocess, run_subprocess + + +GIT_CREDENTIAL_REGEX = re.compile( + r""" + ^\s* # start of line + credential\.helper # credential.helper value + \s*=\s* # separator + ([\w\-\/]+) # the helper name or absolute path (group 1) + (\s|$) # whitespace or end of line + """, + flags=re.MULTILINE | re.IGNORECASE | re.VERBOSE, +) + + +def list_credential_helpers(folder: Optional[str] = None) -> List[str]: + """Return the list of git credential helpers configured. + + See https://git-scm.com/docs/gitcredentials. + + Credentials are saved in all configured helpers (store, cache, macOS keychain,...). + Calls "`git credential approve`" internally. See https://git-scm.com/docs/git-credential. + + Args: + folder (`str`, *optional*): + The folder in which to check the configured helpers. + """ + try: + output = run_subprocess("git config --list", folder=folder).stdout + parsed = _parse_credential_output(output) + return parsed + except subprocess.CalledProcessError as exc: + raise EnvironmentError(exc.stderr) + + +def set_git_credential(token: str, username: str = "hf_user", folder: Optional[str] = None) -> None: + """Save a username/token pair in git credential for HF Hub registry. + + Credentials are saved in all configured helpers (store, cache, macOS keychain,...). + Calls "`git credential approve`" internally. See https://git-scm.com/docs/git-credential. + + Args: + username (`str`, defaults to `"hf_user"`): + A git username. Defaults to `"hf_user"`, the default user used in the Hub. + token (`str`, defaults to `"hf_user"`): + A git password. In practice, the User Access Token for the Hub. + See https://huggingface.co/settings/tokens. + folder (`str`, *optional*): + The folder in which to check the configured helpers. + """ + with run_interactive_subprocess("git credential approve", folder=folder) as ( + stdin, + _, + ): + stdin.write(f"url={ENDPOINT}\nusername={username.lower()}\npassword={token}\n\n") + stdin.flush() + + +def unset_git_credential(username: str = "hf_user", folder: Optional[str] = None) -> None: + """Erase credentials from git credential for HF Hub registry. + + Credentials are erased from the configured helpers (store, cache, macOS + keychain,...), if any. If `username` is not provided, any credential configured for + HF Hub endpoint is erased. + Calls "`git credential erase`" internally. See https://git-scm.com/docs/git-credential. + + Args: + username (`str`, defaults to `"hf_user"`): + A git username. Defaults to `"hf_user"`, the default user used in the Hub. + folder (`str`, *optional*): + The folder in which to check the configured helpers. + """ + with run_interactive_subprocess("git credential reject", folder=folder) as ( + stdin, + _, + ): + standard_input = f"url={ENDPOINT}\n" + if username is not None: + standard_input += f"username={username.lower()}\n" + standard_input += "\n" + + stdin.write(standard_input) + stdin.flush() + + +def _parse_credential_output(output: str) -> List[str]: + """Parse the output of `git credential fill` to extract the password. + + Args: + output (`str`): + The output of `git credential fill`. + """ + # NOTE: If user has set an helper for a custom URL, it will not we caught here. + # Example: `credential.https://huggingface.co.helper=store` + # See: https://github.com/huggingface/huggingface_hub/pull/1138#discussion_r1013324508 + return sorted( # Sort for nice printing + set( # Might have some duplicates + match[0] for match in GIT_CREDENTIAL_REGEX.findall(output) + ) + ) diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/utils/_headers.py b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_headers.py new file mode 100644 index 0000000000000000000000000000000000000000..053a92a398f8734ee14cd67e4b514dfc350fcecd --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_headers.py @@ -0,0 +1,228 @@ +# coding=utf-8 +# Copyright 2022-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains utilities to handle headers to send in calls to Huggingface Hub.""" + +from typing import Dict, Optional, Union + +from huggingface_hub.errors import LocalTokenNotFoundError + +from .. import constants +from ._auth import get_token +from ._deprecation import _deprecate_arguments +from ._runtime import ( + get_fastai_version, + get_fastcore_version, + get_hf_hub_version, + get_python_version, + get_tf_version, + get_torch_version, + is_fastai_available, + is_fastcore_available, + is_tf_available, + is_torch_available, +) +from ._validators import validate_hf_hub_args + + +@_deprecate_arguments( + version="1.0", + deprecated_args="is_write_action", + custom_message="This argument is ignored and we let the server handle the permission error instead (if any).", +) +@validate_hf_hub_args +def build_hf_headers( + *, + token: Optional[Union[bool, str]] = None, + library_name: Optional[str] = None, + library_version: Optional[str] = None, + user_agent: Union[Dict, str, None] = None, + headers: Optional[Dict[str, str]] = None, + is_write_action: bool = False, +) -> Dict[str, str]: + """ + Build headers dictionary to send in a HF Hub call. + + By default, authorization token is always provided either from argument (explicit + use) or retrieved from the cache (implicit use). To explicitly avoid sending the + token to the Hub, set `token=False` or set the `HF_HUB_DISABLE_IMPLICIT_TOKEN` + environment variable. + + In case of an API call that requires write access, an error is thrown if token is + `None` or token is an organization token (starting with `"api_org***"`). + + In addition to the auth header, a user-agent is added to provide information about + the installed packages (versions of python, huggingface_hub, torch, tensorflow, + fastai and fastcore). + + Args: + token (`str`, `bool`, *optional*): + The token to be sent in authorization header for the Hub call: + - if a string, it is used as the Hugging Face token + - if `True`, the token is read from the machine (cache or env variable) + - if `False`, authorization header is not set + - if `None`, the token is read from the machine only except if + `HF_HUB_DISABLE_IMPLICIT_TOKEN` env variable is set. + library_name (`str`, *optional*): + The name of the library that is making the HTTP request. Will be added to + the user-agent header. + library_version (`str`, *optional*): + The version of the library that is making the HTTP request. Will be added + to the user-agent header. + user_agent (`str`, `dict`, *optional*): + The user agent info in the form of a dictionary or a single string. It will + be completed with information about the installed packages. + headers (`dict`, *optional*): + Additional headers to include in the request. Those headers take precedence + over the ones generated by this function. + is_write_action (`bool`): + Ignored and deprecated argument. + + Returns: + A `Dict` of headers to pass in your API call. + + Example: + ```py + >>> build_hf_headers(token="hf_***") # explicit token + {"authorization": "Bearer hf_***", "user-agent": ""} + + >>> build_hf_headers(token=True) # explicitly use cached token + {"authorization": "Bearer hf_***",...} + + >>> build_hf_headers(token=False) # explicitly don't use cached token + {"user-agent": ...} + + >>> build_hf_headers() # implicit use of the cached token + {"authorization": "Bearer hf_***",...} + + # HF_HUB_DISABLE_IMPLICIT_TOKEN=True # to set as env variable + >>> build_hf_headers() # token is not sent + {"user-agent": ...} + + >>> build_hf_headers(library_name="transformers", library_version="1.2.3") + {"authorization": ..., "user-agent": "transformers/1.2.3; hf_hub/0.10.2; python/3.10.4; tensorflow/1.55"} + ``` + + Raises: + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If organization token is passed and "write" access is required. + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If "write" access is required but token is not passed and not saved locally. + [`EnvironmentError`](https://docs.python.org/3/library/exceptions.html#EnvironmentError) + If `token=True` but token is not saved locally. + """ + # Get auth token to send + token_to_send = get_token_to_send(token) + + # Combine headers + hf_headers = { + "user-agent": _http_user_agent( + library_name=library_name, + library_version=library_version, + user_agent=user_agent, + ) + } + if token_to_send is not None: + hf_headers["authorization"] = f"Bearer {token_to_send}" + if headers is not None: + hf_headers.update(headers) + return hf_headers + + +def get_token_to_send(token: Optional[Union[bool, str]]) -> Optional[str]: + """Select the token to send from either `token` or the cache.""" + # Case token is explicitly provided + if isinstance(token, str): + return token + + # Case token is explicitly forbidden + if token is False: + return None + + # Token is not provided: we get it from local cache + cached_token = get_token() + + # Case token is explicitly required + if token is True: + if cached_token is None: + raise LocalTokenNotFoundError( + "Token is required (`token=True`), but no token found. You" + " need to provide a token or be logged in to Hugging Face with" + " `hf auth login` or `huggingface_hub.login`. See" + " https://huggingface.co/settings/tokens." + ) + return cached_token + + # Case implicit use of the token is forbidden by env variable + if constants.HF_HUB_DISABLE_IMPLICIT_TOKEN: + return None + + # Otherwise: we use the cached token as the user has not explicitly forbidden it + return cached_token + + +def _http_user_agent( + *, + library_name: Optional[str] = None, + library_version: Optional[str] = None, + user_agent: Union[Dict, str, None] = None, +) -> str: + """Format a user-agent string containing information about the installed packages. + + Args: + library_name (`str`, *optional*): + The name of the library that is making the HTTP request. + library_version (`str`, *optional*): + The version of the library that is making the HTTP request. + user_agent (`str`, `dict`, *optional*): + The user agent info in the form of a dictionary or a single string. + + Returns: + The formatted user-agent string. + """ + if library_name is not None: + ua = f"{library_name}/{library_version}" + else: + ua = "unknown/None" + ua += f"; hf_hub/{get_hf_hub_version()}" + ua += f"; python/{get_python_version()}" + + if not constants.HF_HUB_DISABLE_TELEMETRY: + if is_torch_available(): + ua += f"; torch/{get_torch_version()}" + if is_tf_available(): + ua += f"; tensorflow/{get_tf_version()}" + if is_fastai_available(): + ua += f"; fastai/{get_fastai_version()}" + if is_fastcore_available(): + ua += f"; fastcore/{get_fastcore_version()}" + + if isinstance(user_agent, dict): + ua += "; " + "; ".join(f"{k}/{v}" for k, v in user_agent.items()) + elif isinstance(user_agent, str): + ua += "; " + user_agent + + # Retrieve user-agent origin headers from environment variable + origin = constants.HF_HUB_USER_AGENT_ORIGIN + if origin is not None: + ua += "; origin/" + origin + + return _deduplicate_user_agent(ua) + + +def _deduplicate_user_agent(user_agent: str) -> str: + """Deduplicate redundant information in the generated user-agent.""" + # Split around ";" > Strip whitespaces > Store as dict keys (ensure unicity) > format back as string + # Order is implicitly preserved by dictionary structure (see https://stackoverflow.com/a/53657523). + return "; ".join({key.strip(): None for key in user_agent.split(";")}.keys()) diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/utils/_hf_folder.py b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_hf_folder.py new file mode 100644 index 0000000000000000000000000000000000000000..6418bf2fd2c59b4bcf301c1dd82bc468f2f42ddf --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_hf_folder.py @@ -0,0 +1,68 @@ +# coding=utf-8 +# Copyright 2022-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contain helper class to retrieve/store token from/to local cache.""" + +from pathlib import Path +from typing import Optional + +from .. import constants +from ._auth import get_token + + +class HfFolder: + # TODO: deprecate when adapted in transformers/datasets/gradio + # @_deprecate_method(version="1.0", message="Use `huggingface_hub.login` instead.") + @classmethod + def save_token(cls, token: str) -> None: + """ + Save token, creating folder as needed. + + Token is saved in the huggingface home folder. You can configure it by setting + the `HF_HOME` environment variable. + + Args: + token (`str`): + The token to save to the [`HfFolder`] + """ + path_token = Path(constants.HF_TOKEN_PATH) + path_token.parent.mkdir(parents=True, exist_ok=True) + path_token.write_text(token) + + # TODO: deprecate when adapted in transformers/datasets/gradio + # @_deprecate_method(version="1.0", message="Use `huggingface_hub.get_token` instead.") + @classmethod + def get_token(cls) -> Optional[str]: + """ + Get token or None if not existent. + + This method is deprecated in favor of [`huggingface_hub.get_token`] but is kept for backward compatibility. + Its behavior is the same as [`huggingface_hub.get_token`]. + + Returns: + `str` or `None`: The token, `None` if it doesn't exist. + """ + return get_token() + + # TODO: deprecate when adapted in transformers/datasets/gradio + # @_deprecate_method(version="1.0", message="Use `huggingface_hub.logout` instead.") + @classmethod + def delete_token(cls) -> None: + """ + Deletes the token from storage. Does not fail if token does not exist. + """ + try: + Path(constants.HF_TOKEN_PATH).unlink() + except FileNotFoundError: + pass diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/utils/_http.py b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_http.py new file mode 100644 index 0000000000000000000000000000000000000000..579c4451ce412422a569aeb0c1151709b5ec0c42 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_http.py @@ -0,0 +1,630 @@ +# coding=utf-8 +# Copyright 2022-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains utilities to handle HTTP requests in Huggingface Hub.""" + +import io +import os +import re +import threading +import time +import uuid +from functools import lru_cache +from shlex import quote +from typing import Any, Callable, List, Optional, Tuple, Type, Union + +import requests +from requests import HTTPError, Response +from requests.adapters import HTTPAdapter +from requests.models import PreparedRequest + +from huggingface_hub.errors import OfflineModeIsEnabled + +from .. import constants +from ..errors import ( + BadRequestError, + DisabledRepoError, + EntryNotFoundError, + GatedRepoError, + HfHubHTTPError, + RepositoryNotFoundError, + RevisionNotFoundError, +) +from . import logging +from ._fixes import JSONDecodeError +from ._lfs import SliceFileObj +from ._typing import HTTP_METHOD_T + + +logger = logging.get_logger(__name__) + +# Both headers are used by the Hub to debug failed requests. +# `X_AMZN_TRACE_ID` is better as it also works to debug on Cloudfront and ALB. +# If `X_AMZN_TRACE_ID` is set, the Hub will use it as well. +X_AMZN_TRACE_ID = "X-Amzn-Trace-Id" +X_REQUEST_ID = "x-request-id" + +REPO_API_REGEX = re.compile( + r""" + # staging or production endpoint + ^https://[^/]+ + ( + # on /api/repo_type/repo_id + /api/(models|datasets|spaces)/(.+) + | + # or /repo_id/resolve/revision/... + /(.+)/resolve/(.+) + ) + """, + flags=re.VERBOSE, +) + + +class UniqueRequestIdAdapter(HTTPAdapter): + X_AMZN_TRACE_ID = "X-Amzn-Trace-Id" + + def add_headers(self, request, **kwargs): + super().add_headers(request, **kwargs) + + # Add random request ID => easier for server-side debug + if X_AMZN_TRACE_ID not in request.headers: + request.headers[X_AMZN_TRACE_ID] = request.headers.get(X_REQUEST_ID) or str(uuid.uuid4()) + + # Add debug log + has_token = len(str(request.headers.get("authorization", ""))) > 0 + logger.debug( + f"Request {request.headers[X_AMZN_TRACE_ID]}: {request.method} {request.url} (authenticated: {has_token})" + ) + + def send(self, request: PreparedRequest, *args, **kwargs) -> Response: + """Catch any RequestException to append request id to the error message for debugging.""" + if constants.HF_DEBUG: + logger.debug(f"Send: {_curlify(request)}") + try: + return super().send(request, *args, **kwargs) + except requests.RequestException as e: + request_id = request.headers.get(X_AMZN_TRACE_ID) + if request_id is not None: + # Taken from https://stackoverflow.com/a/58270258 + e.args = (*e.args, f"(Request ID: {request_id})") + raise + + +class OfflineAdapter(HTTPAdapter): + def send(self, request: PreparedRequest, *args, **kwargs) -> Response: + raise OfflineModeIsEnabled( + f"Cannot reach {request.url}: offline mode is enabled. To disable it, please unset the `HF_HUB_OFFLINE` environment variable." + ) + + +def _default_backend_factory() -> requests.Session: + session = requests.Session() + if constants.HF_HUB_OFFLINE: + session.mount("http://", OfflineAdapter()) + session.mount("https://", OfflineAdapter()) + else: + session.mount("http://", UniqueRequestIdAdapter()) + session.mount("https://", UniqueRequestIdAdapter()) + return session + + +BACKEND_FACTORY_T = Callable[[], requests.Session] +_GLOBAL_BACKEND_FACTORY: BACKEND_FACTORY_T = _default_backend_factory + + +def configure_http_backend(backend_factory: BACKEND_FACTORY_T = _default_backend_factory) -> None: + """ + Configure the HTTP backend by providing a `backend_factory`. Any HTTP calls made by `huggingface_hub` will use a + Session object instantiated by this factory. This can be useful if you are running your scripts in a specific + environment requiring custom configuration (e.g. custom proxy or certifications). + + Use [`get_session`] to get a configured Session. Since `requests.Session` is not guaranteed to be thread-safe, + `huggingface_hub` creates 1 Session instance per thread. They are all instantiated using the same `backend_factory` + set in [`configure_http_backend`]. A LRU cache is used to cache the created sessions (and connections) between + calls. Max size is 128 to avoid memory leaks if thousands of threads are spawned. + + See [this issue](https://github.com/psf/requests/issues/2766) to know more about thread-safety in `requests`. + + Example: + ```py + import requests + from huggingface_hub import configure_http_backend, get_session + + # Create a factory function that returns a Session with configured proxies + def backend_factory() -> requests.Session: + session = requests.Session() + session.proxies = {"http": "http://10.10.1.10:3128", "https": "https://10.10.1.11:1080"} + return session + + # Set it as the default session factory + configure_http_backend(backend_factory=backend_factory) + + # In practice, this is mostly done internally in `huggingface_hub` + session = get_session() + ``` + """ + global _GLOBAL_BACKEND_FACTORY + _GLOBAL_BACKEND_FACTORY = backend_factory + reset_sessions() + + +def get_session() -> requests.Session: + """ + Get a `requests.Session` object, using the session factory from the user. + + Use [`get_session`] to get a configured Session. Since `requests.Session` is not guaranteed to be thread-safe, + `huggingface_hub` creates 1 Session instance per thread. They are all instantiated using the same `backend_factory` + set in [`configure_http_backend`]. A LRU cache is used to cache the created sessions (and connections) between + calls. Max size is 128 to avoid memory leaks if thousands of threads are spawned. + + See [this issue](https://github.com/psf/requests/issues/2766) to know more about thread-safety in `requests`. + + Example: + ```py + import requests + from huggingface_hub import configure_http_backend, get_session + + # Create a factory function that returns a Session with configured proxies + def backend_factory() -> requests.Session: + session = requests.Session() + session.proxies = {"http": "http://10.10.1.10:3128", "https": "https://10.10.1.11:1080"} + return session + + # Set it as the default session factory + configure_http_backend(backend_factory=backend_factory) + + # In practice, this is mostly done internally in `huggingface_hub` + session = get_session() + ``` + """ + return _get_session_from_cache(process_id=os.getpid(), thread_id=threading.get_ident()) + + +def reset_sessions() -> None: + """Reset the cache of sessions. + + Mostly used internally when sessions are reconfigured or an SSLError is raised. + See [`configure_http_backend`] for more details. + """ + _get_session_from_cache.cache_clear() + + +@lru_cache +def _get_session_from_cache(process_id: int, thread_id: int) -> requests.Session: + """ + Create a new session per thread using global factory. Using LRU cache (maxsize 128) to avoid memory leaks when + using thousands of threads. Cache is cleared when `configure_http_backend` is called. + """ + return _GLOBAL_BACKEND_FACTORY() + + +def http_backoff( + method: HTTP_METHOD_T, + url: str, + *, + max_retries: int = 5, + base_wait_time: float = 1, + max_wait_time: float = 8, + retry_on_exceptions: Union[Type[Exception], Tuple[Type[Exception], ...]] = ( + requests.Timeout, + requests.ConnectionError, + requests.exceptions.ChunkedEncodingError, + ), + retry_on_status_codes: Union[int, Tuple[int, ...]] = (500, 502, 503, 504), + **kwargs, +) -> Response: + """Wrapper around requests to retry calls on an endpoint, with exponential backoff. + + Endpoint call is retried on exceptions (ex: connection timeout, proxy error,...) + and/or on specific status codes (ex: service unavailable). If the call failed more + than `max_retries`, the exception is thrown or `raise_for_status` is called on the + response object. + + Re-implement mechanisms from the `backoff` library to avoid adding an external + dependencies to `hugging_face_hub`. See https://github.com/litl/backoff. + + Args: + method (`Literal["GET", "OPTIONS", "HEAD", "POST", "PUT", "PATCH", "DELETE"]`): + HTTP method to perform. + url (`str`): + The URL of the resource to fetch. + max_retries (`int`, *optional*, defaults to `5`): + Maximum number of retries, defaults to 5 (no retries). + base_wait_time (`float`, *optional*, defaults to `1`): + Duration (in seconds) to wait before retrying the first time. + Wait time between retries then grows exponentially, capped by + `max_wait_time`. + max_wait_time (`float`, *optional*, defaults to `8`): + Maximum duration (in seconds) to wait before retrying. + retry_on_exceptions (`Type[Exception]` or `Tuple[Type[Exception]]`, *optional*): + Define which exceptions must be caught to retry the request. Can be a single type or a tuple of types. + By default, retry on `requests.Timeout`, `requests.ConnectionError` and `requests.exceptions.ChunkedEncodingError`. + retry_on_status_codes (`int` or `Tuple[int]`, *optional*, defaults to `(500, 502, 503, 504)`): + Define on which status codes the request must be retried. By default, 5xx errors are retried. + **kwargs (`dict`, *optional*): + kwargs to pass to `requests.request`. + + Example: + ``` + >>> from huggingface_hub.utils import http_backoff + + # Same usage as "requests.request". + >>> response = http_backoff("GET", "https://www.google.com") + >>> response.raise_for_status() + + # If you expect a Gateway Timeout from time to time + >>> http_backoff("PUT", upload_url, data=data, retry_on_status_codes=504) + >>> response.raise_for_status() + ``` + + > [!WARNING] + > When using `requests` it is possible to stream data by passing an iterator to the + > `data` argument. On http backoff this is a problem as the iterator is not reset + > after a failed call. This issue is mitigated for file objects or any IO streams + > by saving the initial position of the cursor (with `data.tell()`) and resetting the + > cursor between each call (with `data.seek()`). For arbitrary iterators, http backoff + > will fail. If this is a hard constraint for you, please let us know by opening an + > issue on [Github](https://github.com/huggingface/huggingface_hub). + """ + if isinstance(retry_on_exceptions, type): # Tuple from single exception type + retry_on_exceptions = (retry_on_exceptions,) + + if isinstance(retry_on_status_codes, int): # Tuple from single status code + retry_on_status_codes = (retry_on_status_codes,) + + nb_tries = 0 + sleep_time = base_wait_time + + # If `data` is used and is a file object (or any IO), it will be consumed on the + # first HTTP request. We need to save the initial position so that the full content + # of the file is re-sent on http backoff. See warning tip in docstring. + io_obj_initial_pos = None + if "data" in kwargs and isinstance(kwargs["data"], (io.IOBase, SliceFileObj)): + io_obj_initial_pos = kwargs["data"].tell() + + session = get_session() + while True: + nb_tries += 1 + try: + # If `data` is used and is a file object (or any IO), set back cursor to + # initial position. + if io_obj_initial_pos is not None: + kwargs["data"].seek(io_obj_initial_pos) + + # Perform request and return if status_code is not in the retry list. + response = session.request(method=method, url=url, **kwargs) + if response.status_code not in retry_on_status_codes: + return response + + # Wrong status code returned (HTTP 503 for instance) + logger.warning(f"HTTP Error {response.status_code} thrown while requesting {method} {url}") + if nb_tries > max_retries: + response.raise_for_status() # Will raise uncaught exception + # We return response to avoid infinite loop in the corner case where the + # user ask for retry on a status code that doesn't raise_for_status. + return response + + except retry_on_exceptions as err: + logger.warning(f"'{err}' thrown while requesting {method} {url}") + + if isinstance(err, requests.ConnectionError): + reset_sessions() # In case of SSLError it's best to reset the shared requests.Session objects + + if nb_tries > max_retries: + raise err + + # Sleep for X seconds + logger.warning(f"Retrying in {sleep_time}s [Retry {nb_tries}/{max_retries}].") + time.sleep(sleep_time) + + # Update sleep time for next retry + sleep_time = min(max_wait_time, sleep_time * 2) # Exponential backoff + + +def fix_hf_endpoint_in_url(url: str, endpoint: Optional[str]) -> str: + """Replace the default endpoint in a URL by a custom one. + + This is useful when using a proxy and the Hugging Face Hub returns a URL with the default endpoint. + """ + endpoint = endpoint.rstrip("/") if endpoint else constants.ENDPOINT + # check if a proxy has been set => if yes, update the returned URL to use the proxy + if endpoint not in (constants._HF_DEFAULT_ENDPOINT, constants._HF_DEFAULT_STAGING_ENDPOINT): + url = url.replace(constants._HF_DEFAULT_ENDPOINT, endpoint) + url = url.replace(constants._HF_DEFAULT_STAGING_ENDPOINT, endpoint) + return url + + +def hf_raise_for_status(response: Response, endpoint_name: Optional[str] = None) -> None: + """ + Internal version of `response.raise_for_status()` that will refine a + potential HTTPError. Raised exception will be an instance of `HfHubHTTPError`. + + This helper is meant to be the unique method to raise_for_status when making a call + to the Hugging Face Hub. + + + Example: + ```py + import requests + from huggingface_hub.utils import get_session, hf_raise_for_status, HfHubHTTPError + + response = get_session().post(...) + try: + hf_raise_for_status(response) + except HfHubHTTPError as e: + print(str(e)) # formatted message + e.request_id, e.server_message # details returned by server + + # Complete the error message with additional information once it's raised + e.append_to_message("\n`create_commit` expects the repository to exist.") + raise + ``` + + Args: + response (`Response`): + Response from the server. + endpoint_name (`str`, *optional*): + Name of the endpoint that has been called. If provided, the error message + will be more complete. + + > [!WARNING] + > Raises when the request has failed: + > + > - [`~utils.RepositoryNotFoundError`] + > If the repository to download from cannot be found. This may be because it + > doesn't exist, because `repo_type` is not set correctly, or because the repo + > is `private` and you do not have access. + > - [`~utils.GatedRepoError`] + > If the repository exists but is gated and the user is not on the authorized + > list. + > - [`~utils.RevisionNotFoundError`] + > If the repository exists but the revision couldn't be find. + > - [`~utils.EntryNotFoundError`] + > If the repository exists but the entry (e.g. the requested file) couldn't be + > find. + > - [`~utils.BadRequestError`] + > If request failed with a HTTP 400 BadRequest error. + > - [`~utils.HfHubHTTPError`] + > If request failed for a reason not listed above. + """ + try: + response.raise_for_status() + except HTTPError as e: + error_code = response.headers.get("X-Error-Code") + error_message = response.headers.get("X-Error-Message") + + if error_code == "RevisionNotFound": + message = f"{response.status_code} Client Error." + "\n\n" + f"Revision Not Found for url: {response.url}." + raise _format(RevisionNotFoundError, message, response) from e + + elif error_code == "EntryNotFound": + message = f"{response.status_code} Client Error." + "\n\n" + f"Entry Not Found for url: {response.url}." + raise _format(EntryNotFoundError, message, response) from e + + elif error_code == "GatedRepo": + message = ( + f"{response.status_code} Client Error." + "\n\n" + f"Cannot access gated repo for url {response.url}." + ) + raise _format(GatedRepoError, message, response) from e + + elif error_message == "Access to this resource is disabled.": + message = ( + f"{response.status_code} Client Error." + + "\n\n" + + f"Cannot access repository for url {response.url}." + + "\n" + + "Access to this resource is disabled." + ) + raise _format(DisabledRepoError, message, response) from e + + elif error_code == "RepoNotFound" or ( + response.status_code == 401 + and error_message != "Invalid credentials in Authorization header" + and response.request is not None + and response.request.url is not None + and REPO_API_REGEX.search(response.request.url) is not None + ): + # 401 is misleading as it is returned for: + # - private and gated repos if user is not authenticated + # - missing repos + # => for now, we process them as `RepoNotFound` anyway. + # See https://gist.github.com/Wauplin/46c27ad266b15998ce56a6603796f0b9 + message = ( + f"{response.status_code} Client Error." + + "\n\n" + + f"Repository Not Found for url: {response.url}." + + "\nPlease make sure you specified the correct `repo_id` and" + " `repo_type`.\nIf you are trying to access a private or gated repo," + " make sure you are authenticated. For more details, see" + " https://huggingface.co/docs/huggingface_hub/authentication" + ) + raise _format(RepositoryNotFoundError, message, response) from e + + elif response.status_code == 400: + message = ( + f"\n\nBad request for {endpoint_name} endpoint:" if endpoint_name is not None else "\n\nBad request:" + ) + raise _format(BadRequestError, message, response) from e + + elif response.status_code == 403: + message = ( + f"\n\n{response.status_code} Forbidden: {error_message}." + + f"\nCannot access content at: {response.url}." + + "\nMake sure your token has the correct permissions." + ) + raise _format(HfHubHTTPError, message, response) from e + + elif response.status_code == 416: + range_header = response.request.headers.get("Range") + message = f"{e}. Requested range: {range_header}. Content-Range: {response.headers.get('Content-Range')}." + raise _format(HfHubHTTPError, message, response) from e + + # Convert `HTTPError` into a `HfHubHTTPError` to display request information + # as well (request id and/or server error message) + raise _format(HfHubHTTPError, str(e), response) from e + + +def _format(error_type: Type[HfHubHTTPError], custom_message: str, response: Response) -> HfHubHTTPError: + server_errors = [] + + # Retrieve server error from header + from_headers = response.headers.get("X-Error-Message") + if from_headers is not None: + server_errors.append(from_headers) + + # Retrieve server error from body + try: + # Case errors are returned in a JSON format + data = response.json() + + error = data.get("error") + if error is not None: + if isinstance(error, list): + # Case {'error': ['my error 1', 'my error 2']} + server_errors.extend(error) + else: + # Case {'error': 'my error'} + server_errors.append(error) + + errors = data.get("errors") + if errors is not None: + # Case {'errors': [{'message': 'my error 1'}, {'message': 'my error 2'}]} + for error in errors: + if "message" in error: + server_errors.append(error["message"]) + + except JSONDecodeError: + # If content is not JSON and not HTML, append the text + content_type = response.headers.get("Content-Type", "") + if response.text and "html" not in content_type.lower(): + server_errors.append(response.text) + + # Strip all server messages + server_errors = [str(line).strip() for line in server_errors if str(line).strip()] + + # Deduplicate server messages (keep order) + # taken from https://stackoverflow.com/a/17016257 + server_errors = list(dict.fromkeys(server_errors)) + + # Format server error + server_message = "\n".join(server_errors) + + # Add server error to custom message + final_error_message = custom_message + if server_message and server_message.lower() not in custom_message.lower(): + if "\n\n" in custom_message: + final_error_message += "\n" + server_message + else: + final_error_message += "\n\n" + server_message + # Add Request ID + request_id = str(response.headers.get(X_REQUEST_ID, "")) + if request_id: + request_id_message = f" (Request ID: {request_id})" + else: + # Fallback to X-Amzn-Trace-Id + request_id = str(response.headers.get(X_AMZN_TRACE_ID, "")) + if request_id: + request_id_message = f" (Amzn Trace ID: {request_id})" + if request_id and request_id.lower() not in final_error_message.lower(): + if "\n" in final_error_message: + newline_index = final_error_message.index("\n") + final_error_message = ( + final_error_message[:newline_index] + request_id_message + final_error_message[newline_index:] + ) + else: + final_error_message += request_id_message + + # Return + return error_type(final_error_message.strip(), response=response, server_message=server_message or None) + + +def _curlify(request: requests.PreparedRequest) -> str: + """Convert a `requests.PreparedRequest` into a curl command (str). + + Used for debug purposes only. + + Implementation vendored from https://github.com/ofw/curlify/blob/master/curlify.py. + MIT License Copyright (c) 2016 Egor. + """ + parts: List[Tuple[Any, Any]] = [ + ("curl", None), + ("-X", request.method), + ] + + for k, v in sorted(request.headers.items()): + if k.lower() == "authorization": + v = "" # Hide authorization header, no matter its value (can be Bearer, Key, etc.) + parts += [("-H", "{0}: {1}".format(k, v))] + + if request.body: + body = request.body + if isinstance(body, bytes): + body = body.decode("utf-8", errors="ignore") + elif hasattr(body, "read"): + body = "" # Don't try to read it to avoid consuming the stream + if len(body) > 1000: + body = body[:1000] + " ... [truncated]" + parts += [("-d", body.replace("\n", ""))] + + parts += [(None, request.url)] + + flat_parts = [] + for k, v in parts: + if k: + flat_parts.append(quote(k)) + if v: + flat_parts.append(quote(v)) + + return " ".join(flat_parts) + + +# Regex to parse HTTP Range header +RANGE_REGEX = re.compile(r"^\s*bytes\s*=\s*(\d*)\s*-\s*(\d*)\s*$", re.IGNORECASE) + + +def _adjust_range_header(original_range: Optional[str], resume_size: int) -> Optional[str]: + """ + Adjust HTTP Range header to account for resume position. + """ + if not original_range: + return f"bytes={resume_size}-" + + if "," in original_range: + raise ValueError(f"Multiple ranges detected - {original_range!r}, not supported yet.") + + match = RANGE_REGEX.match(original_range) + if not match: + raise RuntimeError(f"Invalid range format - {original_range!r}.") + start, end = match.groups() + + if not start: + if not end: + raise RuntimeError(f"Invalid range format - {original_range!r}.") + + new_suffix = int(end) - resume_size + new_range = f"bytes=-{new_suffix}" + if new_suffix <= 0: + raise RuntimeError(f"Empty new range - {new_range!r}.") + return new_range + + start = int(start) + new_start = start + resume_size + if end: + end = int(end) + new_range = f"bytes={new_start}-{end}" + if new_start > end: + raise RuntimeError(f"Empty new range - {new_range!r}.") + return new_range + + return f"bytes={new_start}-" diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/utils/_lfs.py b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_lfs.py new file mode 100644 index 0000000000000000000000000000000000000000..307f371ffa79a8ae726ee03458c52e230a792898 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_lfs.py @@ -0,0 +1,110 @@ +# coding=utf-8 +# Copyright 2019-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Git LFS related utilities""" + +import io +import os +from contextlib import AbstractContextManager +from typing import BinaryIO + + +class SliceFileObj(AbstractContextManager): + """ + Utility context manager to read a *slice* of a seekable file-like object as a seekable, file-like object. + + This is NOT thread safe + + Inspired by stackoverflow.com/a/29838711/593036 + + Credits to @julien-c + + Args: + fileobj (`BinaryIO`): + A file-like object to slice. MUST implement `tell()` and `seek()` (and `read()` of course). + `fileobj` will be reset to its original position when exiting the context manager. + seek_from (`int`): + The start of the slice (offset from position 0 in bytes). + read_limit (`int`): + The maximum number of bytes to read from the slice. + + Attributes: + previous_position (`int`): + The previous position + + Examples: + + Reading 200 bytes with an offset of 128 bytes from a file (ie bytes 128 to 327): + ```python + >>> with open("path/to/file", "rb") as file: + ... with SliceFileObj(file, seek_from=128, read_limit=200) as fslice: + ... fslice.read(...) + ``` + + Reading a file in chunks of 512 bytes + ```python + >>> import os + >>> chunk_size = 512 + >>> file_size = os.getsize("path/to/file") + >>> with open("path/to/file", "rb") as file: + ... for chunk_idx in range(ceil(file_size / chunk_size)): + ... with SliceFileObj(file, seek_from=chunk_idx * chunk_size, read_limit=chunk_size) as fslice: + ... chunk = fslice.read(...) + + ``` + """ + + def __init__(self, fileobj: BinaryIO, seek_from: int, read_limit: int): + self.fileobj = fileobj + self.seek_from = seek_from + self.read_limit = read_limit + + def __enter__(self): + self._previous_position = self.fileobj.tell() + end_of_stream = self.fileobj.seek(0, os.SEEK_END) + self._len = min(self.read_limit, end_of_stream - self.seek_from) + # ^^ The actual number of bytes that can be read from the slice + self.fileobj.seek(self.seek_from, io.SEEK_SET) + return self + + def __exit__(self, exc_type, exc_value, traceback): + self.fileobj.seek(self._previous_position, io.SEEK_SET) + + def read(self, n: int = -1): + pos = self.tell() + if pos >= self._len: + return b"" + remaining_amount = self._len - pos + data = self.fileobj.read(remaining_amount if n < 0 else min(n, remaining_amount)) + return data + + def tell(self) -> int: + return self.fileobj.tell() - self.seek_from + + def seek(self, offset: int, whence: int = os.SEEK_SET) -> int: + start = self.seek_from + end = start + self._len + if whence in (os.SEEK_SET, os.SEEK_END): + offset = start + offset if whence == os.SEEK_SET else end + offset + offset = max(start, min(offset, end)) + whence = os.SEEK_SET + elif whence == os.SEEK_CUR: + cur_pos = self.fileobj.tell() + offset = max(start - cur_pos, min(offset, end - cur_pos)) + else: + raise ValueError(f"whence value {whence} is not supported") + return self.fileobj.seek(offset, whence) - self.seek_from + + def __iter__(self): + yield self.read(n=4 * 1024 * 1024) diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/utils/_pagination.py b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_pagination.py new file mode 100644 index 0000000000000000000000000000000000000000..3ef2b6668ba09d4c6a715509131d157139a1fac0 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_pagination.py @@ -0,0 +1,52 @@ +# coding=utf-8 +# Copyright 2022-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains utilities to handle pagination on Huggingface Hub.""" + +from typing import Dict, Iterable, Optional + +import requests + +from . import get_session, hf_raise_for_status, http_backoff, logging + + +logger = logging.get_logger(__name__) + + +def paginate(path: str, params: Dict, headers: Dict) -> Iterable: + """Fetch a list of models/datasets/spaces and paginate through results. + + This is using the same "Link" header format as GitHub. + See: + - https://requests.readthedocs.io/en/latest/api/#requests.Response.links + - https://docs.github.com/en/rest/guides/traversing-with-pagination#link-header + """ + session = get_session() + r = session.get(path, params=params, headers=headers) + hf_raise_for_status(r) + yield from r.json() + + # Follow pages + # Next link already contains query params + next_page = _get_next_page(r) + while next_page is not None: + logger.debug(f"Pagination detected. Requesting next page: {next_page}") + r = http_backoff("GET", next_page, max_retries=20, retry_on_status_codes=429, headers=headers) + hf_raise_for_status(r) + yield from r.json() + next_page = _get_next_page(r) + + +def _get_next_page(response: requests.Response) -> Optional[str]: + return response.links.get("next", {}).get("url") diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/utils/_paths.py b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_paths.py new file mode 100644 index 0000000000000000000000000000000000000000..4f2c0ebce070bbde4900e919a3aca7cfc331e747 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_paths.py @@ -0,0 +1,141 @@ +# coding=utf-8 +# Copyright 2022-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains utilities to handle paths in Huggingface Hub.""" + +from fnmatch import fnmatch +from pathlib import Path +from typing import Callable, Generator, Iterable, List, Optional, TypeVar, Union + + +T = TypeVar("T") + +# Always ignore `.git` and `.cache/huggingface` folders in commits +DEFAULT_IGNORE_PATTERNS = [ + ".git", + ".git/*", + "*/.git", + "**/.git/**", + ".cache/huggingface", + ".cache/huggingface/*", + "*/.cache/huggingface", + "**/.cache/huggingface/**", +] +# Forbidden to commit these folders +FORBIDDEN_FOLDERS = [".git", ".cache"] + + +def filter_repo_objects( + items: Iterable[T], + *, + allow_patterns: Optional[Union[List[str], str]] = None, + ignore_patterns: Optional[Union[List[str], str]] = None, + key: Optional[Callable[[T], str]] = None, +) -> Generator[T, None, None]: + """Filter repo objects based on an allowlist and a denylist. + + Input must be a list of paths (`str` or `Path`) or a list of arbitrary objects. + In the later case, `key` must be provided and specifies a function of one argument + that is used to extract a path from each element in iterable. + + Patterns are Unix shell-style wildcards which are NOT regular expressions. See + https://docs.python.org/3/library/fnmatch.html for more details. + + Args: + items (`Iterable`): + List of items to filter. + allow_patterns (`str` or `List[str]`, *optional*): + Patterns constituting the allowlist. If provided, item paths must match at + least one pattern from the allowlist. + ignore_patterns (`str` or `List[str]`, *optional*): + Patterns constituting the denylist. If provided, item paths must not match + any patterns from the denylist. + key (`Callable[[T], str]`, *optional*): + Single-argument function to extract a path from each item. If not provided, + the `items` must already be `str` or `Path`. + + Returns: + Filtered list of objects, as a generator. + + Raises: + :class:`ValueError`: + If `key` is not provided and items are not `str` or `Path`. + + Example usage with paths: + ```python + >>> # Filter only PDFs that are not hidden. + >>> list(filter_repo_objects( + ... ["aaa.PDF", "bbb.jpg", ".ccc.pdf", ".ddd.png"], + ... allow_patterns=["*.pdf"], + ... ignore_patterns=[".*"], + ... )) + ["aaa.pdf"] + ``` + + Example usage with objects: + ```python + >>> list(filter_repo_objects( + ... [ + ... CommitOperationAdd(path_or_fileobj="/tmp/aaa.pdf", path_in_repo="aaa.pdf") + ... CommitOperationAdd(path_or_fileobj="/tmp/bbb.jpg", path_in_repo="bbb.jpg") + ... CommitOperationAdd(path_or_fileobj="/tmp/.ccc.pdf", path_in_repo=".ccc.pdf") + ... CommitOperationAdd(path_or_fileobj="/tmp/.ddd.png", path_in_repo=".ddd.png") + ... ], + ... allow_patterns=["*.pdf"], + ... ignore_patterns=[".*"], + ... key=lambda x: x.repo_in_path + ... )) + [CommitOperationAdd(path_or_fileobj="/tmp/aaa.pdf", path_in_repo="aaa.pdf")] + ``` + """ + if isinstance(allow_patterns, str): + allow_patterns = [allow_patterns] + + if isinstance(ignore_patterns, str): + ignore_patterns = [ignore_patterns] + + if allow_patterns is not None: + allow_patterns = [_add_wildcard_to_directories(p) for p in allow_patterns] + if ignore_patterns is not None: + ignore_patterns = [_add_wildcard_to_directories(p) for p in ignore_patterns] + + if key is None: + + def _identity(item: T) -> str: + if isinstance(item, str): + return item + if isinstance(item, Path): + return str(item) + raise ValueError(f"Please provide `key` argument in `filter_repo_objects`: `{item}` is not a string.") + + key = _identity # Items must be `str` or `Path`, otherwise raise ValueError + + for item in items: + path = key(item) + + # Skip if there's an allowlist and path doesn't match any + if allow_patterns is not None and not any(fnmatch(path, r) for r in allow_patterns): + continue + + # Skip if there's a denylist and path matches any + if ignore_patterns is not None and any(fnmatch(path, r) for r in ignore_patterns): + continue + + yield item + + +def _add_wildcard_to_directories(pattern: str) -> str: + if pattern[-1] == "/": + return pattern + "*" + return pattern diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/utils/_runtime.py b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_runtime.py new file mode 100644 index 0000000000000000000000000000000000000000..9e38e6da7493074703032150b8b7d6766ed0fed6 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_runtime.py @@ -0,0 +1,395 @@ +# coding=utf-8 +# Copyright 2022-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Check presence of installed packages at runtime.""" + +import importlib.metadata +import os +import platform +import sys +import warnings +from typing import Any, Dict + +from .. import __version__, constants + + +_PY_VERSION: str = sys.version.split()[0].rstrip("+") + +_package_versions = {} + +_CANDIDATES = { + "aiohttp": {"aiohttp"}, + "fastai": {"fastai"}, + "fastapi": {"fastapi"}, + "fastcore": {"fastcore"}, + "gradio": {"gradio"}, + "graphviz": {"graphviz"}, + "hf_transfer": {"hf_transfer"}, + "hf_xet": {"hf_xet"}, + "jinja": {"Jinja2"}, + "keras": {"keras"}, + "numpy": {"numpy"}, + "pillow": {"Pillow"}, + "pydantic": {"pydantic"}, + "pydot": {"pydot"}, + "safetensors": {"safetensors"}, + "tensorboard": {"tensorboardX"}, + "tensorflow": ( + "tensorflow", + "tensorflow-cpu", + "tensorflow-gpu", + "tf-nightly", + "tf-nightly-cpu", + "tf-nightly-gpu", + "intel-tensorflow", + "intel-tensorflow-avx512", + "tensorflow-rocm", + "tensorflow-macos", + ), + "torch": {"torch"}, +} + +# Check once at runtime +for candidate_name, package_names in _CANDIDATES.items(): + _package_versions[candidate_name] = "N/A" + for name in package_names: + try: + _package_versions[candidate_name] = importlib.metadata.version(name) + break + except importlib.metadata.PackageNotFoundError: + pass + + +def _get_version(package_name: str) -> str: + return _package_versions.get(package_name, "N/A") + + +def is_package_available(package_name: str) -> bool: + return _get_version(package_name) != "N/A" + + +# Python +def get_python_version() -> str: + return _PY_VERSION + + +# Huggingface Hub +def get_hf_hub_version() -> str: + return __version__ + + +# aiohttp +def is_aiohttp_available() -> bool: + return is_package_available("aiohttp") + + +def get_aiohttp_version() -> str: + return _get_version("aiohttp") + + +# FastAI +def is_fastai_available() -> bool: + return is_package_available("fastai") + + +def get_fastai_version() -> str: + return _get_version("fastai") + + +# FastAPI +def is_fastapi_available() -> bool: + return is_package_available("fastapi") + + +def get_fastapi_version() -> str: + return _get_version("fastapi") + + +# Fastcore +def is_fastcore_available() -> bool: + return is_package_available("fastcore") + + +def get_fastcore_version() -> str: + return _get_version("fastcore") + + +# FastAI +def is_gradio_available() -> bool: + return is_package_available("gradio") + + +def get_gradio_version() -> str: + return _get_version("gradio") + + +# Graphviz +def is_graphviz_available() -> bool: + return is_package_available("graphviz") + + +def get_graphviz_version() -> str: + return _get_version("graphviz") + + +# hf_transfer +def is_hf_transfer_available() -> bool: + return is_package_available("hf_transfer") + + +def get_hf_transfer_version() -> str: + return _get_version("hf_transfer") + + +# xet +def is_xet_available() -> bool: + # since hf_xet is automatically used if available, allow explicit disabling via environment variable + if constants.HF_HUB_DISABLE_XET: + return False + + return is_package_available("hf_xet") + + +def get_xet_version() -> str: + return _get_version("hf_xet") + + +# keras +def is_keras_available() -> bool: + return is_package_available("keras") + + +def get_keras_version() -> str: + return _get_version("keras") + + +# Numpy +def is_numpy_available() -> bool: + return is_package_available("numpy") + + +def get_numpy_version() -> str: + return _get_version("numpy") + + +# Jinja +def is_jinja_available() -> bool: + return is_package_available("jinja") + + +def get_jinja_version() -> str: + return _get_version("jinja") + + +# Pillow +def is_pillow_available() -> bool: + return is_package_available("pillow") + + +def get_pillow_version() -> str: + return _get_version("pillow") + + +# Pydantic +def is_pydantic_available() -> bool: + if not is_package_available("pydantic"): + return False + # For Pydantic, we add an extra check to test whether it is correctly installed or not. If both pydantic 2.x and + # typing_extensions<=4.5.0 are installed, then pydantic will fail at import time. This should not happen when + # it is installed with `pip install huggingface_hub[inference]` but it can happen when it is installed manually + # by the user in an environment that we don't control. + # + # Usually we won't need to do this kind of check on optional dependencies. However, pydantic is a special case + # as it is automatically imported when doing `from huggingface_hub import ...` even if the user doesn't use it. + # + # See https://github.com/huggingface/huggingface_hub/pull/1829 for more details. + try: + from pydantic import validator # noqa: F401 + except ImportError: + # Example: "ImportError: cannot import name 'TypeAliasType' from 'typing_extensions'" + warnings.warn( + "Pydantic is installed but cannot be imported. Please check your installation. `huggingface_hub` will " + "default to not using Pydantic. Error message: '{e}'" + ) + return False + return True + + +def get_pydantic_version() -> str: + return _get_version("pydantic") + + +# Pydot +def is_pydot_available() -> bool: + return is_package_available("pydot") + + +def get_pydot_version() -> str: + return _get_version("pydot") + + +# Tensorboard +def is_tensorboard_available() -> bool: + return is_package_available("tensorboard") + + +def get_tensorboard_version() -> str: + return _get_version("tensorboard") + + +# Tensorflow +def is_tf_available() -> bool: + return is_package_available("tensorflow") + + +def get_tf_version() -> str: + return _get_version("tensorflow") + + +# Torch +def is_torch_available() -> bool: + return is_package_available("torch") + + +def get_torch_version() -> str: + return _get_version("torch") + + +# Safetensors +def is_safetensors_available() -> bool: + return is_package_available("safetensors") + + +# Shell-related helpers +try: + # Set to `True` if script is running in a Google Colab notebook. + # If running in Google Colab, git credential store is set globally which makes the + # warning disappear. See https://github.com/huggingface/huggingface_hub/issues/1043 + # + # Taken from https://stackoverflow.com/a/63519730. + _is_google_colab = "google.colab" in str(get_ipython()) # type: ignore # noqa: F821 +except NameError: + _is_google_colab = False + + +def is_notebook() -> bool: + """Return `True` if code is executed in a notebook (Jupyter, Colab, QTconsole). + + Taken from https://stackoverflow.com/a/39662359. + Adapted to make it work with Google colab as well. + """ + try: + shell_class = get_ipython().__class__ # type: ignore # noqa: F821 + for parent_class in shell_class.__mro__: # e.g. "is subclass of" + if parent_class.__name__ == "ZMQInteractiveShell": + return True # Jupyter notebook, Google colab or qtconsole + return False + except NameError: + return False # Probably standard Python interpreter + + +def is_google_colab() -> bool: + """Return `True` if code is executed in a Google colab. + + Taken from https://stackoverflow.com/a/63519730. + """ + return _is_google_colab + + +def is_colab_enterprise() -> bool: + """Return `True` if code is executed in a Google Colab Enterprise environment.""" + return os.environ.get("VERTEX_PRODUCT") == "COLAB_ENTERPRISE" + + +def dump_environment_info() -> Dict[str, Any]: + """Dump information about the machine to help debugging issues. + + Similar helper exist in: + - `datasets` (https://github.com/huggingface/datasets/blob/main/src/datasets/commands/env.py) + - `diffusers` (https://github.com/huggingface/diffusers/blob/main/src/diffusers/commands/env.py) + - `transformers` (https://github.com/huggingface/transformers/blob/main/src/transformers/commands/env.py) + """ + from huggingface_hub import get_token, whoami + from huggingface_hub.utils import list_credential_helpers + + token = get_token() + + # Generic machine info + info: Dict[str, Any] = { + "huggingface_hub version": get_hf_hub_version(), + "Platform": platform.platform(), + "Python version": get_python_version(), + } + + # Interpreter info + try: + shell_class = get_ipython().__class__ # type: ignore # noqa: F821 + info["Running in iPython ?"] = "Yes" + info["iPython shell"] = shell_class.__name__ + except NameError: + info["Running in iPython ?"] = "No" + info["Running in notebook ?"] = "Yes" if is_notebook() else "No" + info["Running in Google Colab ?"] = "Yes" if is_google_colab() else "No" + info["Running in Google Colab Enterprise ?"] = "Yes" if is_colab_enterprise() else "No" + # Login info + info["Token path ?"] = constants.HF_TOKEN_PATH + info["Has saved token ?"] = token is not None + if token is not None: + try: + info["Who am I ?"] = whoami()["name"] + except Exception: + pass + + try: + info["Configured git credential helpers"] = ", ".join(list_credential_helpers()) + except Exception: + pass + + # Installed dependencies + info["FastAI"] = get_fastai_version() + info["Tensorflow"] = get_tf_version() + info["Torch"] = get_torch_version() + info["Jinja2"] = get_jinja_version() + info["Graphviz"] = get_graphviz_version() + info["keras"] = get_keras_version() + info["Pydot"] = get_pydot_version() + info["Pillow"] = get_pillow_version() + info["hf_transfer"] = get_hf_transfer_version() + info["gradio"] = get_gradio_version() + info["tensorboard"] = get_tensorboard_version() + info["numpy"] = get_numpy_version() + info["pydantic"] = get_pydantic_version() + info["aiohttp"] = get_aiohttp_version() + info["hf_xet"] = get_xet_version() + + # Environment variables + info["ENDPOINT"] = constants.ENDPOINT + info["HF_HUB_CACHE"] = constants.HF_HUB_CACHE + info["HF_ASSETS_CACHE"] = constants.HF_ASSETS_CACHE + info["HF_TOKEN_PATH"] = constants.HF_TOKEN_PATH + info["HF_STORED_TOKENS_PATH"] = constants.HF_STORED_TOKENS_PATH + info["HF_HUB_OFFLINE"] = constants.HF_HUB_OFFLINE + info["HF_HUB_DISABLE_TELEMETRY"] = constants.HF_HUB_DISABLE_TELEMETRY + info["HF_HUB_DISABLE_PROGRESS_BARS"] = constants.HF_HUB_DISABLE_PROGRESS_BARS + info["HF_HUB_DISABLE_SYMLINKS_WARNING"] = constants.HF_HUB_DISABLE_SYMLINKS_WARNING + info["HF_HUB_DISABLE_EXPERIMENTAL_WARNING"] = constants.HF_HUB_DISABLE_EXPERIMENTAL_WARNING + info["HF_HUB_DISABLE_IMPLICIT_TOKEN"] = constants.HF_HUB_DISABLE_IMPLICIT_TOKEN + info["HF_HUB_DISABLE_XET"] = constants.HF_HUB_DISABLE_XET + info["HF_HUB_ENABLE_HF_TRANSFER"] = constants.HF_HUB_ENABLE_HF_TRANSFER + info["HF_HUB_ETAG_TIMEOUT"] = constants.HF_HUB_ETAG_TIMEOUT + info["HF_HUB_DOWNLOAD_TIMEOUT"] = constants.HF_HUB_DOWNLOAD_TIMEOUT + + print("\nCopy-and-paste the text below in your GitHub issue.\n") + print("\n".join([f"- {prop}: {val}" for prop, val in info.items()]) + "\n") + return info diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/utils/_safetensors.py b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_safetensors.py new file mode 100644 index 0000000000000000000000000000000000000000..38546c6d34db786c62861e1706f747a21b7012bf --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_safetensors.py @@ -0,0 +1,111 @@ +import functools +import operator +from collections import defaultdict +from dataclasses import dataclass, field +from typing import Dict, List, Literal, Optional, Tuple + + +FILENAME_T = str +TENSOR_NAME_T = str +DTYPE_T = Literal["F64", "F32", "F16", "BF16", "I64", "I32", "I16", "I8", "U8", "BOOL"] + + +@dataclass +class TensorInfo: + """Information about a tensor. + + For more details regarding the safetensors format, check out https://huggingface.co/docs/safetensors/index#format. + + Attributes: + dtype (`str`): + The data type of the tensor ("F64", "F32", "F16", "BF16", "I64", "I32", "I16", "I8", "U8", "BOOL"). + shape (`List[int]`): + The shape of the tensor. + data_offsets (`Tuple[int, int]`): + The offsets of the data in the file as a tuple `[BEGIN, END]`. + parameter_count (`int`): + The number of parameters in the tensor. + """ + + dtype: DTYPE_T + shape: List[int] + data_offsets: Tuple[int, int] + parameter_count: int = field(init=False) + + def __post_init__(self) -> None: + # Taken from https://stackoverflow.com/a/13840436 + try: + self.parameter_count = functools.reduce(operator.mul, self.shape) + except TypeError: + self.parameter_count = 1 # scalar value has no shape + + +@dataclass +class SafetensorsFileMetadata: + """Metadata for a Safetensors file hosted on the Hub. + + This class is returned by [`parse_safetensors_file_metadata`]. + + For more details regarding the safetensors format, check out https://huggingface.co/docs/safetensors/index#format. + + Attributes: + metadata (`Dict`): + The metadata contained in the file. + tensors (`Dict[str, TensorInfo]`): + A map of all tensors. Keys are tensor names and values are information about the corresponding tensor, as a + [`TensorInfo`] object. + parameter_count (`Dict[str, int]`): + A map of the number of parameters per data type. Keys are data types and values are the number of parameters + of that data type. + """ + + metadata: Dict[str, str] + tensors: Dict[TENSOR_NAME_T, TensorInfo] + parameter_count: Dict[DTYPE_T, int] = field(init=False) + + def __post_init__(self) -> None: + parameter_count: Dict[DTYPE_T, int] = defaultdict(int) + for tensor in self.tensors.values(): + parameter_count[tensor.dtype] += tensor.parameter_count + self.parameter_count = dict(parameter_count) + + +@dataclass +class SafetensorsRepoMetadata: + """Metadata for a Safetensors repo. + + A repo is considered to be a Safetensors repo if it contains either a 'model.safetensors' weight file (non-shared + model) or a 'model.safetensors.index.json' index file (sharded model) at its root. + + This class is returned by [`get_safetensors_metadata`]. + + For more details regarding the safetensors format, check out https://huggingface.co/docs/safetensors/index#format. + + Attributes: + metadata (`Dict`, *optional*): + The metadata contained in the 'model.safetensors.index.json' file, if it exists. Only populated for sharded + models. + sharded (`bool`): + Whether the repo contains a sharded model or not. + weight_map (`Dict[str, str]`): + A map of all weights. Keys are tensor names and values are filenames of the files containing the tensors. + files_metadata (`Dict[str, SafetensorsFileMetadata]`): + A map of all files metadata. Keys are filenames and values are the metadata of the corresponding file, as + a [`SafetensorsFileMetadata`] object. + parameter_count (`Dict[str, int]`): + A map of the number of parameters per data type. Keys are data types and values are the number of parameters + of that data type. + """ + + metadata: Optional[Dict] + sharded: bool + weight_map: Dict[TENSOR_NAME_T, FILENAME_T] # tensor name -> filename + files_metadata: Dict[FILENAME_T, SafetensorsFileMetadata] # filename -> metadata + parameter_count: Dict[DTYPE_T, int] = field(init=False) + + def __post_init__(self) -> None: + parameter_count: Dict[DTYPE_T, int] = defaultdict(int) + for file_metadata in self.files_metadata.values(): + for dtype, nb_parameters_ in file_metadata.parameter_count.items(): + parameter_count[dtype] += nb_parameters_ + self.parameter_count = dict(parameter_count) diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/utils/_subprocess.py b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_subprocess.py new file mode 100644 index 0000000000000000000000000000000000000000..fdabf1c4df3b61dc610ae08eb7842df6af3552f3 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_subprocess.py @@ -0,0 +1,144 @@ +# coding=utf-8 +# Copyright 2021 The HuggingFace Inc. team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License +"""Contains utilities to easily handle subprocesses in `huggingface_hub`.""" + +import os +import subprocess +import sys +from contextlib import contextmanager +from io import StringIO +from pathlib import Path +from typing import IO, Generator, List, Optional, Tuple, Union + +from .logging import get_logger + + +logger = get_logger(__name__) + + +@contextmanager +def capture_output() -> Generator[StringIO, None, None]: + """Capture output that is printed to terminal. + + Taken from https://stackoverflow.com/a/34738440 + + Example: + ```py + >>> with capture_output() as output: + ... print("hello world") + >>> assert output.getvalue() == "hello world\n" + ``` + """ + output = StringIO() + previous_output = sys.stdout + sys.stdout = output + try: + yield output + finally: + sys.stdout = previous_output + + +def run_subprocess( + command: Union[str, List[str]], + folder: Optional[Union[str, Path]] = None, + check=True, + **kwargs, +) -> subprocess.CompletedProcess: + """ + Method to run subprocesses. Calling this will capture the `stderr` and `stdout`, + please call `subprocess.run` manually in case you would like for them not to + be captured. + + Args: + command (`str` or `List[str]`): + The command to execute as a string or list of strings. + folder (`str`, *optional*): + The folder in which to run the command. Defaults to current working + directory (from `os.getcwd()`). + check (`bool`, *optional*, defaults to `True`): + Setting `check` to `True` will raise a `subprocess.CalledProcessError` + when the subprocess has a non-zero exit code. + kwargs (`Dict[str]`): + Keyword arguments to be passed to the `subprocess.run` underlying command. + + Returns: + `subprocess.CompletedProcess`: The completed process. + """ + if isinstance(command, str): + command = command.split() + + if isinstance(folder, Path): + folder = str(folder) + + return subprocess.run( + command, + stderr=subprocess.PIPE, + stdout=subprocess.PIPE, + check=check, + encoding="utf-8", + errors="replace", # if not utf-8, replace char by � + cwd=folder or os.getcwd(), + **kwargs, + ) + + +@contextmanager +def run_interactive_subprocess( + command: Union[str, List[str]], + folder: Optional[Union[str, Path]] = None, + **kwargs, +) -> Generator[Tuple[IO[str], IO[str]], None, None]: + """Run a subprocess in an interactive mode in a context manager. + + Args: + command (`str` or `List[str]`): + The command to execute as a string or list of strings. + folder (`str`, *optional*): + The folder in which to run the command. Defaults to current working + directory (from `os.getcwd()`). + kwargs (`Dict[str]`): + Keyword arguments to be passed to the `subprocess.run` underlying command. + + Returns: + `Tuple[IO[str], IO[str]]`: A tuple with `stdin` and `stdout` to interact + with the process (input and output are utf-8 encoded). + + Example: + ```python + with _interactive_subprocess("git credential-store get") as (stdin, stdout): + # Write to stdin + stdin.write("url=hf.co\nusername=obama\n".encode("utf-8")) + stdin.flush() + + # Read from stdout + output = stdout.read().decode("utf-8") + ``` + """ + if isinstance(command, str): + command = command.split() + + with subprocess.Popen( + command, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + encoding="utf-8", + errors="replace", # if not utf-8, replace char by � + cwd=folder or os.getcwd(), + **kwargs, + ) as process: + assert process.stdin is not None, "subprocess is opened as subprocess.PIPE" + assert process.stdout is not None, "subprocess is opened as subprocess.PIPE" + yield process.stdin, process.stdout diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/utils/_telemetry.py b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_telemetry.py new file mode 100644 index 0000000000000000000000000000000000000000..2ba4a6349a8de1c565263ec73d235d36f88b68cf --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_telemetry.py @@ -0,0 +1,126 @@ +from queue import Queue +from threading import Lock, Thread +from typing import Dict, Optional, Union +from urllib.parse import quote + +from .. import constants, logging +from . import build_hf_headers, get_session, hf_raise_for_status + + +logger = logging.get_logger(__name__) + +# Telemetry is sent by a separate thread to avoid blocking the main thread. +# A daemon thread is started once and consume tasks from the _TELEMETRY_QUEUE. +# If the thread stops for some reason -shouldn't happen-, we restart a new one. +_TELEMETRY_THREAD: Optional[Thread] = None +_TELEMETRY_THREAD_LOCK = Lock() # Lock to avoid starting multiple threads in parallel +_TELEMETRY_QUEUE: Queue = Queue() + + +def send_telemetry( + topic: str, + *, + library_name: Optional[str] = None, + library_version: Optional[str] = None, + user_agent: Union[Dict, str, None] = None, +) -> None: + """ + Sends telemetry that helps tracking usage of different HF libraries. + + This usage data helps us debug issues and prioritize new features. However, we understand that not everyone wants + to share additional information, and we respect your privacy. You can disable telemetry collection by setting the + `HF_HUB_DISABLE_TELEMETRY=1` as environment variable. Telemetry is also disabled in offline mode (i.e. when setting + `HF_HUB_OFFLINE=1`). + + Telemetry collection is run in a separate thread to minimize impact for the user. + + Args: + topic (`str`): + Name of the topic that is monitored. The topic is directly used to build the URL. If you want to monitor + subtopics, just use "/" separation. Examples: "gradio", "transformers/examples",... + library_name (`str`, *optional*): + The name of the library that is making the HTTP request. Will be added to the user-agent header. + library_version (`str`, *optional*): + The version of the library that is making the HTTP request. Will be added to the user-agent header. + user_agent (`str`, `dict`, *optional*): + The user agent info in the form of a dictionary or a single string. It will be completed with information about the installed packages. + + Example: + ```py + >>> from huggingface_hub.utils import send_telemetry + + # Send telemetry without library information + >>> send_telemetry("ping") + + # Send telemetry to subtopic with library information + >>> send_telemetry("gradio/local_link", library_name="gradio", library_version="3.22.1") + + # Send telemetry with additional data + >>> send_telemetry( + ... topic="examples", + ... library_name="transformers", + ... library_version="4.26.0", + ... user_agent={"pipeline": "text_classification", "framework": "flax"}, + ... ) + ``` + """ + if constants.HF_HUB_OFFLINE or constants.HF_HUB_DISABLE_TELEMETRY: + return + + _start_telemetry_thread() # starts thread only if doesn't exist yet + _TELEMETRY_QUEUE.put( + {"topic": topic, "library_name": library_name, "library_version": library_version, "user_agent": user_agent} + ) + + +def _start_telemetry_thread(): + """Start a daemon thread to consume tasks from the telemetry queue. + + If the thread is interrupted, start a new one. + """ + with _TELEMETRY_THREAD_LOCK: # avoid to start multiple threads if called concurrently + global _TELEMETRY_THREAD + if _TELEMETRY_THREAD is None or not _TELEMETRY_THREAD.is_alive(): + _TELEMETRY_THREAD = Thread(target=_telemetry_worker, daemon=True) + _TELEMETRY_THREAD.start() + + +def _telemetry_worker(): + """Wait for a task and consume it.""" + while True: + kwargs = _TELEMETRY_QUEUE.get() + _send_telemetry_in_thread(**kwargs) + _TELEMETRY_QUEUE.task_done() + + +def _send_telemetry_in_thread( + topic: str, + *, + library_name: Optional[str] = None, + library_version: Optional[str] = None, + user_agent: Union[Dict, str, None] = None, +) -> None: + """Contains the actual data sending data to the Hub. + + This function is called directly in gradio's analytics because + it is not possible to send telemetry from a daemon thread. + + See here: https://github.com/gradio-app/gradio/pull/8180 + + Please do not rename or remove this function. + """ + path = "/".join(quote(part) for part in topic.split("/") if len(part) > 0) + try: + r = get_session().head( + f"{constants.ENDPOINT}/api/telemetry/{path}", + headers=build_hf_headers( + token=False, # no need to send a token for telemetry + library_name=library_name, + library_version=library_version, + user_agent=user_agent, + ), + ) + hf_raise_for_status(r) + except Exception as e: + # We don't want to error in case of connection errors of any kind. + logger.debug(f"Error while sending telemetry: {e}") diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/utils/_typing.py b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_typing.py new file mode 100644 index 0000000000000000000000000000000000000000..8c5d6381a2a73afa08698bb99193f1774fb02f64 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_typing.py @@ -0,0 +1,95 @@ +# coding=utf-8 +# Copyright 2022-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Handle typing imports based on system compatibility.""" + +import sys +from typing import Any, Callable, List, Literal, Optional, Set, Type, TypeVar, Union, get_args, get_origin + + +UNION_TYPES: List[Any] = [Union] +if sys.version_info >= (3, 10): + from types import UnionType + + UNION_TYPES += [UnionType] + + +HTTP_METHOD_T = Literal["GET", "OPTIONS", "HEAD", "POST", "PUT", "PATCH", "DELETE"] + +# type hint meaning "function signature not changed by decorator" +CallableT = TypeVar("CallableT", bound=Callable) + +_JSON_SERIALIZABLE_TYPES = (int, float, str, bool, type(None)) + + +def is_jsonable(obj: Any, _visited: Optional[Set[int]] = None) -> bool: + """Check if an object is JSON serializable. + + This is a weak check, as it does not check for the actual JSON serialization, but only for the types of the object. + It works correctly for basic use cases but do not guarantee an exhaustive check. + + Object is considered to be recursively json serializable if: + - it is an instance of int, float, str, bool, or NoneType + - it is a list or tuple and all its items are json serializable + - it is a dict and all its keys are strings and all its values are json serializable + + Uses a visited set to avoid infinite recursion on circular references. If object has already been visited, it is + considered not json serializable. + """ + # Initialize visited set to track object ids and detect circular references + if _visited is None: + _visited = set() + + # Detect circular reference + obj_id = id(obj) + if obj_id in _visited: + return False + + # Add current object to visited before recursive checks + _visited.add(obj_id) + try: + if isinstance(obj, _JSON_SERIALIZABLE_TYPES): + return True + if isinstance(obj, (list, tuple)): + return all(is_jsonable(item, _visited) for item in obj) + if isinstance(obj, dict): + return all( + isinstance(key, _JSON_SERIALIZABLE_TYPES) and is_jsonable(value, _visited) + for key, value in obj.items() + ) + if hasattr(obj, "__json__"): + return True + return False + except RecursionError: + return False + finally: + # Remove the object id from visited to avoid side‑effects for other branches + _visited.discard(obj_id) + + +def is_simple_optional_type(type_: Type) -> bool: + """Check if a type is optional, i.e. Optional[Type] or Union[Type, None] or Type | None, where Type is a non-composite type.""" + if get_origin(type_) in UNION_TYPES: + union_args = get_args(type_) + if len(union_args) == 2 and type(None) in union_args: + return True + return False + + +def unwrap_simple_optional_type(optional_type: Type) -> Type: + """Unwraps a simple optional type, i.e. returns Type from Optional[Type].""" + for arg in get_args(optional_type): + if arg is not type(None): + return arg + raise ValueError(f"'{optional_type}' is not an optional type") diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/utils/_validators.py b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_validators.py new file mode 100644 index 0000000000000000000000000000000000000000..4bc219611b2132d699643975d00cf99853e03e47 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_validators.py @@ -0,0 +1,226 @@ +# coding=utf-8 +# Copyright 2022-present, the HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Contains utilities to validate argument values in `huggingface_hub`.""" + +import inspect +import re +import warnings +from functools import wraps +from itertools import chain +from typing import Any, Dict + +from huggingface_hub.errors import HFValidationError + +from ._typing import CallableT + + +REPO_ID_REGEX = re.compile( + r""" + ^ + (\b[\w\-.]+\b/)? # optional namespace (username or organization) + \b # starts with a word boundary + [\w\-.]{1,96} # repo_name: alphanumeric + . _ - + \b # ends with a word boundary + $ + """, + flags=re.VERBOSE, +) + + +def validate_hf_hub_args(fn: CallableT) -> CallableT: + """Validate values received as argument for any public method of `huggingface_hub`. + + The goal of this decorator is to harmonize validation of arguments reused + everywhere. By default, all defined validators are tested. + + Validators: + - [`~utils.validate_repo_id`]: `repo_id` must be `"repo_name"` + or `"namespace/repo_name"`. Namespace is a username or an organization. + - [`~utils.smoothly_deprecate_use_auth_token`]: Use `token` instead of + `use_auth_token` (only if `use_auth_token` is not expected by the decorated + function - in practice, always the case in `huggingface_hub`). + + Example: + ```py + >>> from huggingface_hub.utils import validate_hf_hub_args + + >>> @validate_hf_hub_args + ... def my_cool_method(repo_id: str): + ... print(repo_id) + + >>> my_cool_method(repo_id="valid_repo_id") + valid_repo_id + + >>> my_cool_method("other..repo..id") + huggingface_hub.utils._validators.HFValidationError: Cannot have -- or .. in repo_id: 'other..repo..id'. + + >>> my_cool_method(repo_id="other..repo..id") + huggingface_hub.utils._validators.HFValidationError: Cannot have -- or .. in repo_id: 'other..repo..id'. + + >>> @validate_hf_hub_args + ... def my_cool_auth_method(token: str): + ... print(token) + + >>> my_cool_auth_method(token="a token") + "a token" + + >>> my_cool_auth_method(use_auth_token="a use_auth_token") + "a use_auth_token" + + >>> my_cool_auth_method(token="a token", use_auth_token="a use_auth_token") + UserWarning: Both `token` and `use_auth_token` are passed (...) + "a token" + ``` + + Raises: + [`~utils.HFValidationError`]: + If an input is not valid. + """ + # TODO: add an argument to opt-out validation for specific argument? + signature = inspect.signature(fn) + + # Should the validator switch `use_auth_token` values to `token`? In practice, always + # True in `huggingface_hub`. Might not be the case in a downstream library. + check_use_auth_token = "use_auth_token" not in signature.parameters and "token" in signature.parameters + + @wraps(fn) + def _inner_fn(*args, **kwargs): + has_token = False + for arg_name, arg_value in chain( + zip(signature.parameters, args), # Args values + kwargs.items(), # Kwargs values + ): + if arg_name in ["repo_id", "from_id", "to_id"]: + validate_repo_id(arg_value) + + elif arg_name == "token" and arg_value is not None: + has_token = True + + if check_use_auth_token: + kwargs = smoothly_deprecate_use_auth_token(fn_name=fn.__name__, has_token=has_token, kwargs=kwargs) + + return fn(*args, **kwargs) + + return _inner_fn # type: ignore + + +def validate_repo_id(repo_id: str) -> None: + """Validate `repo_id` is valid. + + This is not meant to replace the proper validation made on the Hub but rather to + avoid local inconsistencies whenever possible (example: passing `repo_type` in the + `repo_id` is forbidden). + + Rules: + - Between 1 and 96 characters. + - Either "repo_name" or "namespace/repo_name" + - [a-zA-Z0-9] or "-", "_", "." + - "--" and ".." are forbidden + + Valid: `"foo"`, `"foo/bar"`, `"123"`, `"Foo-BAR_foo.bar123"` + + Not valid: `"datasets/foo/bar"`, `".repo_id"`, `"foo--bar"`, `"foo.git"` + + Example: + ```py + >>> from huggingface_hub.utils import validate_repo_id + >>> validate_repo_id(repo_id="valid_repo_id") + >>> validate_repo_id(repo_id="other..repo..id") + huggingface_hub.utils._validators.HFValidationError: Cannot have -- or .. in repo_id: 'other..repo..id'. + ``` + + Discussed in https://github.com/huggingface/huggingface_hub/issues/1008. + In moon-landing (internal repository): + - https://github.com/huggingface/moon-landing/blob/main/server/lib/Names.ts#L27 + - https://github.com/huggingface/moon-landing/blob/main/server/views/components/NewRepoForm/NewRepoForm.svelte#L138 + """ + if not isinstance(repo_id, str): + # Typically, a Path is not a repo_id + raise HFValidationError(f"Repo id must be a string, not {type(repo_id)}: '{repo_id}'.") + + if repo_id.count("/") > 1: + raise HFValidationError( + "Repo id must be in the form 'repo_name' or 'namespace/repo_name':" + f" '{repo_id}'. Use `repo_type` argument if needed." + ) + + if not REPO_ID_REGEX.match(repo_id): + raise HFValidationError( + "Repo id must use alphanumeric chars, '-', '_' or '.'." + " The name cannot start or end with '-' or '.' and the maximum length is 96:" + f" '{repo_id}'." + ) + + if "--" in repo_id or ".." in repo_id: + raise HFValidationError(f"Cannot have -- or .. in repo_id: '{repo_id}'.") + + if repo_id.endswith(".git"): + raise HFValidationError(f"Repo_id cannot end by '.git': '{repo_id}'.") + + +def smoothly_deprecate_use_auth_token(fn_name: str, has_token: bool, kwargs: Dict[str, Any]) -> Dict[str, Any]: + """Smoothly deprecate `use_auth_token` in the `huggingface_hub` codebase. + + The long-term goal is to remove any mention of `use_auth_token` in the codebase in + favor of a unique and less verbose `token` argument. This will be done a few steps: + + 0. Step 0: methods that require a read-access to the Hub use the `use_auth_token` + argument (`str`, `bool` or `None`). Methods requiring write-access have a `token` + argument (`str`, `None`). This implicit rule exists to be able to not send the + token when not necessary (`use_auth_token=False`) even if logged in. + + 1. Step 1: we want to harmonize everything and use `token` everywhere (supporting + `token=False` for read-only methods). In order not to break existing code, if + `use_auth_token` is passed to a function, the `use_auth_token` value is passed + as `token` instead, without any warning. + a. Corner case: if both `use_auth_token` and `token` values are passed, a warning + is thrown and the `use_auth_token` value is ignored. + + 2. Step 2: Once it is release, we should push downstream libraries to switch from + `use_auth_token` to `token` as much as possible, but without throwing a warning + (e.g. manually create issues on the corresponding repos). + + 3. Step 3: After a transitional period (6 months e.g. until April 2023?), we update + `huggingface_hub` to throw a warning on `use_auth_token`. Hopefully, very few + users will be impacted as it would have already been fixed. + In addition, unit tests in `huggingface_hub` must be adapted to expect warnings + to be thrown (but still use `use_auth_token` as before). + + 4. Step 4: After a normal deprecation cycle (3 releases ?), remove this validator. + `use_auth_token` will definitely not be supported. + In addition, we update unit tests in `huggingface_hub` to use `token` everywhere. + + This has been discussed in: + - https://github.com/huggingface/huggingface_hub/issues/1094. + - https://github.com/huggingface/huggingface_hub/pull/928 + - (related) https://github.com/huggingface/huggingface_hub/pull/1064 + """ + new_kwargs = kwargs.copy() # do not mutate input ! + + use_auth_token = new_kwargs.pop("use_auth_token", None) # remove from kwargs + if use_auth_token is not None: + if has_token: + warnings.warn( + "Both `token` and `use_auth_token` are passed to" + f" `{fn_name}` with non-None values. `token` is now the" + " preferred argument to pass a User Access Token." + " `use_auth_token` value will be ignored." + ) + else: + # `token` argument is not passed and a non-None value is passed in + # `use_auth_token` => use `use_auth_token` value as `token` kwarg. + new_kwargs["token"] = use_auth_token + + return new_kwargs diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/utils/_xet.py b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_xet.py new file mode 100644 index 0000000000000000000000000000000000000000..3dcf99068f87eebdf3c684edf6026a576bd34eaf --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_xet.py @@ -0,0 +1,192 @@ +from dataclasses import dataclass +from enum import Enum +from typing import Dict, Optional + +import requests + +from .. import constants +from . import get_session, hf_raise_for_status, validate_hf_hub_args + + +class XetTokenType(str, Enum): + READ = "read" + WRITE = "write" + + +@dataclass(frozen=True) +class XetFileData: + file_hash: str + refresh_route: str + + +@dataclass(frozen=True) +class XetConnectionInfo: + access_token: str + expiration_unix_epoch: int + endpoint: str + + +def parse_xet_file_data_from_response( + response: requests.Response, endpoint: Optional[str] = None +) -> Optional[XetFileData]: + """ + Parse XET file metadata from an HTTP response. + + This function extracts XET file metadata from the HTTP headers or HTTP links + of a given response object. If the required metadata is not found, it returns `None`. + + Args: + response (`requests.Response`): + The HTTP response object containing headers dict and links dict to extract the XET metadata from. + Returns: + `Optional[XetFileData]`: + An instance of `XetFileData` containing the file hash and refresh route if the metadata + is found. Returns `None` if the required metadata is missing. + """ + if response is None: + return None + try: + file_hash = response.headers[constants.HUGGINGFACE_HEADER_X_XET_HASH] + + if constants.HUGGINGFACE_HEADER_LINK_XET_AUTH_KEY in response.links: + refresh_route = response.links[constants.HUGGINGFACE_HEADER_LINK_XET_AUTH_KEY]["url"] + else: + refresh_route = response.headers[constants.HUGGINGFACE_HEADER_X_XET_REFRESH_ROUTE] + except KeyError: + return None + endpoint = endpoint if endpoint is not None else constants.ENDPOINT + if refresh_route.startswith(constants.HUGGINGFACE_CO_URL_HOME): + refresh_route = refresh_route.replace(constants.HUGGINGFACE_CO_URL_HOME.rstrip("/"), endpoint.rstrip("/")) + return XetFileData( + file_hash=file_hash, + refresh_route=refresh_route, + ) + + +def parse_xet_connection_info_from_headers(headers: Dict[str, str]) -> Optional[XetConnectionInfo]: + """ + Parse XET connection info from the HTTP headers or return None if not found. + Args: + headers (`Dict`): + HTTP headers to extract the XET metadata from. + Returns: + `XetConnectionInfo` or `None`: + The information needed to connect to the XET storage service. + Returns `None` if the headers do not contain the XET connection info. + """ + try: + endpoint = headers[constants.HUGGINGFACE_HEADER_X_XET_ENDPOINT] + access_token = headers[constants.HUGGINGFACE_HEADER_X_XET_ACCESS_TOKEN] + expiration_unix_epoch = int(headers[constants.HUGGINGFACE_HEADER_X_XET_EXPIRATION]) + except (KeyError, ValueError, TypeError): + return None + + return XetConnectionInfo( + endpoint=endpoint, + access_token=access_token, + expiration_unix_epoch=expiration_unix_epoch, + ) + + +@validate_hf_hub_args +def refresh_xet_connection_info( + *, + file_data: XetFileData, + headers: Dict[str, str], +) -> XetConnectionInfo: + """ + Utilizes the information in the parsed metadata to request the Hub xet connection information. + This includes the access token, expiration, and XET service URL. + Args: + file_data: (`XetFileData`): + The file data needed to refresh the xet connection information. + headers (`Dict[str, str]`): + Headers to use for the request, including authorization headers and user agent. + Returns: + `XetConnectionInfo`: + The connection information needed to make the request to the xet storage service. + Raises: + [`~utils.HfHubHTTPError`] + If the Hub API returned an error. + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If the Hub API response is improperly formatted. + """ + if file_data.refresh_route is None: + raise ValueError("The provided xet metadata does not contain a refresh endpoint.") + return _fetch_xet_connection_info_with_url(file_data.refresh_route, headers) + + +@validate_hf_hub_args +def fetch_xet_connection_info_from_repo_info( + *, + token_type: XetTokenType, + repo_id: str, + repo_type: str, + revision: Optional[str] = None, + headers: Dict[str, str], + endpoint: Optional[str] = None, + params: Optional[Dict[str, str]] = None, +) -> XetConnectionInfo: + """ + Uses the repo info to request a xet access token from Hub. + Args: + token_type (`XetTokenType`): + Type of the token to request: `"read"` or `"write"`. + repo_id (`str`): + A namespace (user or an organization) and a repo name separated by a `/`. + repo_type (`str`): + Type of the repo to upload to: `"model"`, `"dataset"` or `"space"`. + revision (`str`, `optional`): + The revision of the repo to get the token for. + headers (`Dict[str, str]`): + Headers to use for the request, including authorization headers and user agent. + endpoint (`str`, `optional`): + The endpoint to use for the request. Defaults to the Hub endpoint. + params (`Dict[str, str]`, `optional`): + Additional parameters to pass with the request. + Returns: + `XetConnectionInfo`: + The connection information needed to make the request to the xet storage service. + Raises: + [`~utils.HfHubHTTPError`] + If the Hub API returned an error. + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If the Hub API response is improperly formatted. + """ + endpoint = endpoint if endpoint is not None else constants.ENDPOINT + url = f"{endpoint}/api/{repo_type}s/{repo_id}/xet-{token_type.value}-token/{revision}" + return _fetch_xet_connection_info_with_url(url, headers, params) + + +@validate_hf_hub_args +def _fetch_xet_connection_info_with_url( + url: str, + headers: Dict[str, str], + params: Optional[Dict[str, str]] = None, +) -> XetConnectionInfo: + """ + Requests the xet connection info from the supplied URL. This includes the + access token, expiration time, and endpoint to use for the xet storage service. + Args: + url: (`str`): + The access token endpoint URL. + headers (`Dict[str, str]`): + Headers to use for the request, including authorization headers and user agent. + params (`Dict[str, str]`, `optional`): + Additional parameters to pass with the request. + Returns: + `XetConnectionInfo`: + The connection information needed to make the request to the xet storage service. + Raises: + [`~utils.HfHubHTTPError`] + If the Hub API returned an error. + [`ValueError`](https://docs.python.org/3/library/exceptions.html#ValueError) + If the Hub API response is improperly formatted. + """ + resp = get_session().get(headers=headers, url=url, params=params) + hf_raise_for_status(resp) + + metadata = parse_xet_connection_info_from_headers(resp.headers) # type: ignore + if metadata is None: + raise ValueError("Xet headers have not been correctly set by the server.") + return metadata diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/utils/_xet_progress_reporting.py b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_xet_progress_reporting.py new file mode 100644 index 0000000000000000000000000000000000000000..e47740d5c5ea27253debc9e29c1eae9d10a6034f --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/utils/_xet_progress_reporting.py @@ -0,0 +1,162 @@ +from collections import OrderedDict +from typing import List + +from hf_xet import PyItemProgressUpdate, PyTotalProgressUpdate + +from . import is_google_colab, is_notebook +from .tqdm import tqdm + + +class XetProgressReporter: + """ + Reports on progress for Xet uploads. + + Shows summary progress bars when running in notebooks or GUIs, and detailed per-file progress in console environments. + """ + + def __init__(self, n_lines: int = 10, description_width: int = 30): + self.n_lines = n_lines + self.description_width = description_width + + self.per_file_progress = is_google_colab() or not is_notebook() + + self.tqdm_settings = { + "unit": "B", + "unit_scale": True, + "leave": True, + "unit_divisor": 1000, + "nrows": n_lines + 3 if self.per_file_progress else 3, + "miniters": 1, + "bar_format": "{l_bar}{bar}| {n_fmt:>5}B / {total_fmt:>5}B{postfix:>12}", + } + + # Overall progress bars + self.data_processing_bar = tqdm( + total=0, desc=self.format_desc("Processing Files (0 / 0)", False), position=0, **self.tqdm_settings + ) + + self.upload_bar = tqdm( + total=0, desc=self.format_desc("New Data Upload", False), position=1, **self.tqdm_settings + ) + + self.known_items: set[str] = set() + self.completed_items: set[str] = set() + + # Item bars (scrolling view) + self.item_state: OrderedDict[str, PyItemProgressUpdate] = OrderedDict() + self.current_bars: List = [None] * self.n_lines + + def format_desc(self, name: str, indent: bool) -> str: + """ + if name is longer than width characters, prints ... at the start and then the last width-3 characters of the name, otherwise + the whole name right justified into description_width characters. Also adds some padding. + """ + + if not self.per_file_progress: + # Here we just use the defaults. + return name + + padding = " " if indent else "" + width = self.description_width - len(padding) + + if len(name) > width: + name = f"...{name[-(width - 3) :]}" + + return f"{padding}{name.ljust(width)}" + + def update_progress(self, total_update: PyTotalProgressUpdate, item_updates: List[PyItemProgressUpdate]): + # Update all the per-item values. + for item in item_updates: + item_name = item.item_name + + self.known_items.add(item_name) + + # Only care about items where the processing has already started. + if item.bytes_completed == 0: + continue + + # Overwrite the existing value in there. + self.item_state[item_name] = item + + bar_idx = 0 + new_completed = [] + + # Now, go through and update all the bars + for name, item in self.item_state.items(): + # Is this ready to be removed on the next update? + if item.bytes_completed == item.total_bytes: + self.completed_items.add(name) + new_completed.append(name) + + # If we're only showing summary information, then don't update the individual bars + if not self.per_file_progress: + continue + + # If we've run out of bars to use, then collapse the last ones together. + if bar_idx >= len(self.current_bars): + bar = self.current_bars[-1] + in_final_bar_mode = True + final_bar_aggregation_count = bar_idx + 1 - len(self.current_bars) + else: + bar = self.current_bars[bar_idx] + in_final_bar_mode = False + + if bar is None: + self.current_bars[bar_idx] = tqdm( + desc=self.format_desc(name, True), + position=2 + bar_idx, # Set to the position past the initial bars. + total=item.total_bytes, + initial=item.bytes_completed, + **self.tqdm_settings, + ) + + elif in_final_bar_mode: + bar.n += item.bytes_completed + bar.total += item.total_bytes + bar.set_description(self.format_desc(f"[+ {final_bar_aggregation_count} files]", True), refresh=False) + else: + bar.set_description(self.format_desc(name, True), refresh=False) + bar.n = item.bytes_completed + bar.total = item.total_bytes + + bar_idx += 1 + + # Remove all the completed ones from the ordered dictionary + for name in new_completed: + # Only remove ones from consideration to make room for more items coming in. + if len(self.item_state) <= self.n_lines: + break + + del self.item_state[name] + + if self.per_file_progress: + # Now manually refresh each of the bars + for bar in self.current_bars: + if bar: + bar.refresh() + + # Update overall bars + def postfix(speed): + s = tqdm.format_sizeof(speed) if speed is not None else "???" + return f"{s}B/s ".rjust(10, " ") + + self.data_processing_bar.total = total_update.total_bytes + self.data_processing_bar.set_description( + self.format_desc(f"Processing Files ({len(self.completed_items)} / {len(self.known_items)})", False), + refresh=False, + ) + self.data_processing_bar.set_postfix_str(postfix(total_update.total_bytes_completion_rate), refresh=False) + self.data_processing_bar.update(total_update.total_bytes_completion_increment) + + self.upload_bar.total = total_update.total_transfer_bytes + self.upload_bar.set_postfix_str(postfix(total_update.total_transfer_bytes_completion_rate), refresh=False) + self.upload_bar.update(total_update.total_transfer_bytes_completion_increment) + + def close(self, _success): + self.data_processing_bar.close() + self.upload_bar.close() + + if self.per_file_progress: + for bar in self.current_bars: + if bar: + bar.close() diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/utils/endpoint_helpers.py b/venv/lib/python3.13/site-packages/huggingface_hub/utils/endpoint_helpers.py new file mode 100644 index 0000000000000000000000000000000000000000..85cd86011b78bcdc57034aeebc3c01e9e721ab50 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/utils/endpoint_helpers.py @@ -0,0 +1,66 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Helpful utility functions and classes in relation to exploring API endpoints +with the aim for a user-friendly interface. +""" + +import math +import re +from typing import TYPE_CHECKING + +from ..repocard_data import ModelCardData + + +if TYPE_CHECKING: + from ..hf_api import ModelInfo + + +def _is_emission_within_threshold(model_info: "ModelInfo", minimum_threshold: float, maximum_threshold: float) -> bool: + """Checks if a model's emission is within a given threshold. + + Args: + model_info (`ModelInfo`): + A model info object containing the model's emission information. + minimum_threshold (`float`): + A minimum carbon threshold to filter by, such as 1. + maximum_threshold (`float`): + A maximum carbon threshold to filter by, such as 10. + + Returns: + `bool`: Whether the model's emission is within the given threshold. + """ + if minimum_threshold is None and maximum_threshold is None: + raise ValueError("Both `minimum_threshold` and `maximum_threshold` cannot both be `None`") + if minimum_threshold is None: + minimum_threshold = -1 + if maximum_threshold is None: + maximum_threshold = math.inf + + card_data = getattr(model_info, "card_data", None) + if card_data is None or not isinstance(card_data, (dict, ModelCardData)): + return False + + # Get CO2 emission metadata + emission = card_data.get("co2_eq_emissions", None) + if isinstance(emission, dict): + emission = emission["emissions"] + if not emission: + return False + + # Filter out if value is missing or out of range + matched = re.search(r"\d+\.\d+|\d+", str(emission)) + if matched is None: + return False + + emission_value = float(matched.group(0)) + return minimum_threshold <= emission_value <= maximum_threshold diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/utils/insecure_hashlib.py b/venv/lib/python3.13/site-packages/huggingface_hub/utils/insecure_hashlib.py new file mode 100644 index 0000000000000000000000000000000000000000..6901b6d647cc706b85333a66f3bcb7d8c5e2ee9e --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/utils/insecure_hashlib.py @@ -0,0 +1,38 @@ +# Taken from https://github.com/mlflow/mlflow/pull/10119 +# +# DO NOT use this function for security purposes (e.g., password hashing). +# +# In Python >= 3.9, insecure hashing algorithms such as MD5 fail in FIPS-compliant +# environments unless `usedforsecurity=False` is explicitly passed. +# +# References: +# - https://github.com/mlflow/mlflow/issues/9905 +# - https://github.com/mlflow/mlflow/pull/10119 +# - https://docs.python.org/3/library/hashlib.html +# - https://github.com/huggingface/transformers/pull/27038 +# +# Usage: +# ```python +# # Use +# from huggingface_hub.utils.insecure_hashlib import sha256 +# # instead of +# from hashlib import sha256 +# +# # Use +# from huggingface_hub.utils import insecure_hashlib +# # instead of +# import hashlib +# ``` +import functools +import hashlib +import sys + + +if sys.version_info >= (3, 9): + md5 = functools.partial(hashlib.md5, usedforsecurity=False) + sha1 = functools.partial(hashlib.sha1, usedforsecurity=False) + sha256 = functools.partial(hashlib.sha256, usedforsecurity=False) +else: + md5 = hashlib.md5 + sha1 = hashlib.sha1 + sha256 = hashlib.sha256 diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/utils/logging.py b/venv/lib/python3.13/site-packages/huggingface_hub/utils/logging.py new file mode 100644 index 0000000000000000000000000000000000000000..1e2f8ded83074b251a72c83edcf33205808250b9 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/utils/logging.py @@ -0,0 +1,185 @@ +# coding=utf-8 +# Copyright 2020 Optuna, Hugging Face +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Logging utilities.""" + +import logging +import os +from logging import ( + CRITICAL, # NOQA + DEBUG, # NOQA + ERROR, # NOQA + FATAL, # NOQA + INFO, # NOQA + NOTSET, # NOQA + WARN, # NOQA + WARNING, # NOQA +) +from typing import Optional + +from .. import constants + + +log_levels = { + "debug": logging.DEBUG, + "info": logging.INFO, + "warning": logging.WARNING, + "error": logging.ERROR, + "critical": logging.CRITICAL, +} + +_default_log_level = logging.WARNING + + +def _get_library_name() -> str: + return __name__.split(".")[0] + + +def _get_library_root_logger() -> logging.Logger: + return logging.getLogger(_get_library_name()) + + +def _get_default_logging_level(): + """ + If `HF_HUB_VERBOSITY` env var is set to one of the valid choices return that as the new default level. If it is not + - fall back to `_default_log_level` + """ + env_level_str = os.getenv("HF_HUB_VERBOSITY", None) + if env_level_str: + if env_level_str in log_levels: + return log_levels[env_level_str] + else: + logging.getLogger().warning( + f"Unknown option HF_HUB_VERBOSITY={env_level_str}, has to be one of: {', '.join(log_levels.keys())}" + ) + return _default_log_level + + +def _configure_library_root_logger() -> None: + library_root_logger = _get_library_root_logger() + library_root_logger.addHandler(logging.StreamHandler()) + library_root_logger.setLevel(_get_default_logging_level()) + + +def _reset_library_root_logger() -> None: + library_root_logger = _get_library_root_logger() + library_root_logger.setLevel(logging.NOTSET) + + +def get_logger(name: Optional[str] = None) -> logging.Logger: + """ + Returns a logger with the specified name. This function is not supposed + to be directly accessed by library users. + + Args: + name (`str`, *optional*): + The name of the logger to get, usually the filename + + Example: + + ```python + >>> from huggingface_hub import get_logger + + >>> logger = get_logger(__file__) + >>> logger.set_verbosity_info() + ``` + """ + + if name is None: + name = _get_library_name() + + return logging.getLogger(name) + + +def get_verbosity() -> int: + """Return the current level for the HuggingFace Hub's root logger. + + Returns: + Logging level, e.g., `huggingface_hub.logging.DEBUG` and + `huggingface_hub.logging.INFO`. + + > [!TIP] + > HuggingFace Hub has following logging levels: + > + > - `huggingface_hub.logging.CRITICAL`, `huggingface_hub.logging.FATAL` + > - `huggingface_hub.logging.ERROR` + > - `huggingface_hub.logging.WARNING`, `huggingface_hub.logging.WARN` + > - `huggingface_hub.logging.INFO` + > - `huggingface_hub.logging.DEBUG` + """ + return _get_library_root_logger().getEffectiveLevel() + + +def set_verbosity(verbosity: int) -> None: + """ + Sets the level for the HuggingFace Hub's root logger. + + Args: + verbosity (`int`): + Logging level, e.g., `huggingface_hub.logging.DEBUG` and + `huggingface_hub.logging.INFO`. + """ + _get_library_root_logger().setLevel(verbosity) + + +def set_verbosity_info(): + """ + Sets the verbosity to `logging.INFO`. + """ + return set_verbosity(INFO) + + +def set_verbosity_warning(): + """ + Sets the verbosity to `logging.WARNING`. + """ + return set_verbosity(WARNING) + + +def set_verbosity_debug(): + """ + Sets the verbosity to `logging.DEBUG`. + """ + return set_verbosity(DEBUG) + + +def set_verbosity_error(): + """ + Sets the verbosity to `logging.ERROR`. + """ + return set_verbosity(ERROR) + + +def disable_propagation() -> None: + """ + Disable propagation of the library log outputs. Note that log propagation is + disabled by default. + """ + _get_library_root_logger().propagate = False + + +def enable_propagation() -> None: + """ + Enable propagation of the library log outputs. Please disable the + HuggingFace Hub's default handler to prevent double logging if the root + logger has been configured. + """ + _get_library_root_logger().propagate = True + + +_configure_library_root_logger() + +if constants.HF_DEBUG: + # If `HF_DEBUG` environment variable is set, set the verbosity of `huggingface_hub` logger to `DEBUG`. + set_verbosity_debug() diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/utils/sha.py b/venv/lib/python3.13/site-packages/huggingface_hub/utils/sha.py new file mode 100644 index 0000000000000000000000000000000000000000..001c3fe8b2f37a64e890888ca3d521c10ec8f03b --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/utils/sha.py @@ -0,0 +1,64 @@ +"""Utilities to efficiently compute the SHA 256 hash of a bunch of bytes.""" + +from typing import BinaryIO, Optional + +from .insecure_hashlib import sha1, sha256 + + +def sha_fileobj(fileobj: BinaryIO, chunk_size: Optional[int] = None) -> bytes: + """ + Computes the sha256 hash of the given file object, by chunks of size `chunk_size`. + + Args: + fileobj (file-like object): + The File object to compute sha256 for, typically obtained with `open(path, "rb")` + chunk_size (`int`, *optional*): + The number of bytes to read from `fileobj` at once, defaults to 1MB. + + Returns: + `bytes`: `fileobj`'s sha256 hash as bytes + """ + chunk_size = chunk_size if chunk_size is not None else 1024 * 1024 + + sha = sha256() + while True: + chunk = fileobj.read(chunk_size) + sha.update(chunk) + if not chunk: + break + return sha.digest() + + +def git_hash(data: bytes) -> str: + """ + Computes the git-sha1 hash of the given bytes, using the same algorithm as git. + + This is equivalent to running `git hash-object`. See https://git-scm.com/docs/git-hash-object + for more details. + + Note: this method is valid for regular files. For LFS files, the proper git hash is supposed to be computed on the + pointer file content, not the actual file content. However, for simplicity, we directly compare the sha256 of + the LFS file content when we want to compare LFS files. + + Args: + data (`bytes`): + The data to compute the git-hash for. + + Returns: + `str`: the git-hash of `data` as an hexadecimal string. + + Example: + ```python + >>> from huggingface_hub.utils.sha import git_hash + >>> git_hash(b"Hello, World!") + 'b45ef6fec89518d314f546fd6c3025367b721684' + ``` + """ + # Taken from https://gist.github.com/msabramo/763200 + # Note: no need to optimize by reading the file in chunks as we're not supposed to hash huge files (5MB maximum). + sha = sha1() + sha.update(b"blob ") + sha.update(str(len(data)).encode()) + sha.update(b"\0") + sha.update(data) + return sha.hexdigest() diff --git a/venv/lib/python3.13/site-packages/huggingface_hub/utils/tqdm.py b/venv/lib/python3.13/site-packages/huggingface_hub/utils/tqdm.py new file mode 100644 index 0000000000000000000000000000000000000000..4c1fcef4beb73bae13c57b3f66c5828e775b7cd9 --- /dev/null +++ b/venv/lib/python3.13/site-packages/huggingface_hub/utils/tqdm.py @@ -0,0 +1,307 @@ +# coding=utf-8 +# Copyright 2021 The HuggingFace Inc. team. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License +"""Utility helpers to handle progress bars in `huggingface_hub`. + +Example: + 1. Use `huggingface_hub.utils.tqdm` as you would use `tqdm.tqdm` or `tqdm.auto.tqdm`. + 2. To disable progress bars, either use `disable_progress_bars()` helper or set the + environment variable `HF_HUB_DISABLE_PROGRESS_BARS` to 1. + 3. To re-enable progress bars, use `enable_progress_bars()`. + 4. To check whether progress bars are disabled, use `are_progress_bars_disabled()`. + +NOTE: Environment variable `HF_HUB_DISABLE_PROGRESS_BARS` has the priority. + +Example: + ```py + >>> from huggingface_hub.utils import are_progress_bars_disabled, disable_progress_bars, enable_progress_bars, tqdm + + # Disable progress bars globally + >>> disable_progress_bars() + + # Use as normal `tqdm` + >>> for _ in tqdm(range(5)): + ... pass + + # Still not showing progress bars, as `disable=False` is overwritten to `True`. + >>> for _ in tqdm(range(5), disable=False): + ... pass + + >>> are_progress_bars_disabled() + True + + # Re-enable progress bars globally + >>> enable_progress_bars() + + # Progress bar will be shown ! + >>> for _ in tqdm(range(5)): + ... pass + 100%|███████████████████████████████████████| 5/5 [00:00<00:00, 117817.53it/s] + ``` + +Group-based control: + ```python + # Disable progress bars for a specific group + >>> disable_progress_bars("peft.foo") + + # Check state of different groups + >>> assert not are_progress_bars_disabled("peft")) + >>> assert not are_progress_bars_disabled("peft.something") + >>> assert are_progress_bars_disabled("peft.foo")) + >>> assert are_progress_bars_disabled("peft.foo.bar")) + + # Enable progress bars for a subgroup + >>> enable_progress_bars("peft.foo.bar") + + # Check if enabling a subgroup affects the parent group + >>> assert are_progress_bars_disabled("peft.foo")) + >>> assert not are_progress_bars_disabled("peft.foo.bar")) + + # No progress bar for `name="peft.foo"` + >>> for _ in tqdm(range(5), name="peft.foo"): + ... pass + + # Progress bar will be shown for `name="peft.foo.bar"` + >>> for _ in tqdm(range(5), name="peft.foo.bar"): + ... pass + 100%|███████████████████████████████████████| 5/5 [00:00<00:00, 117817.53it/s] + + ``` +""" + +import io +import logging +import os +import warnings +from contextlib import contextmanager, nullcontext +from pathlib import Path +from typing import ContextManager, Dict, Iterator, Optional, Union + +from tqdm.auto import tqdm as old_tqdm + +from ..constants import HF_HUB_DISABLE_PROGRESS_BARS + + +# The `HF_HUB_DISABLE_PROGRESS_BARS` environment variable can be True, False, or not set (None), +# allowing for control over progress bar visibility. When set, this variable takes precedence +# over programmatic settings, dictating whether progress bars should be shown or hidden globally. +# Essentially, the environment variable's setting overrides any code-based configurations. +# +# If `HF_HUB_DISABLE_PROGRESS_BARS` is not defined (None), it implies that users can manage +# progress bar visibility through code. By default, progress bars are turned on. + + +progress_bar_states: Dict[str, bool] = {} + + +def disable_progress_bars(name: Optional[str] = None) -> None: + """ + Disable progress bars either globally or for a specified group. + + This function updates the state of progress bars based on a group name. + If no group name is provided, all progress bars are disabled. The operation + respects the `HF_HUB_DISABLE_PROGRESS_BARS` environment variable's setting. + + Args: + name (`str`, *optional*): + The name of the group for which to disable the progress bars. If None, + progress bars are disabled globally. + + Raises: + Warning: If the environment variable precludes changes. + """ + if HF_HUB_DISABLE_PROGRESS_BARS is False: + warnings.warn( + "Cannot disable progress bars: environment variable `HF_HUB_DISABLE_PROGRESS_BARS=0` is set and has priority." + ) + return + + if name is None: + progress_bar_states.clear() + progress_bar_states["_global"] = False + else: + keys_to_remove = [key for key in progress_bar_states if key.startswith(f"{name}.")] + for key in keys_to_remove: + del progress_bar_states[key] + progress_bar_states[name] = False + + +def enable_progress_bars(name: Optional[str] = None) -> None: + """ + Enable progress bars either globally or for a specified group. + + This function sets the progress bars to enabled for the specified group or globally + if no group is specified. The operation is subject to the `HF_HUB_DISABLE_PROGRESS_BARS` + environment setting. + + Args: + name (`str`, *optional*): + The name of the group for which to enable the progress bars. If None, + progress bars are enabled globally. + + Raises: + Warning: If the environment variable precludes changes. + """ + if HF_HUB_DISABLE_PROGRESS_BARS is True: + warnings.warn( + "Cannot enable progress bars: environment variable `HF_HUB_DISABLE_PROGRESS_BARS=1` is set and has priority." + ) + return + + if name is None: + progress_bar_states.clear() + progress_bar_states["_global"] = True + else: + keys_to_remove = [key for key in progress_bar_states if key.startswith(f"{name}.")] + for key in keys_to_remove: + del progress_bar_states[key] + progress_bar_states[name] = True + + +def are_progress_bars_disabled(name: Optional[str] = None) -> bool: + """ + Check if progress bars are disabled globally or for a specific group. + + This function returns whether progress bars are disabled for a given group or globally. + It checks the `HF_HUB_DISABLE_PROGRESS_BARS` environment variable first, then the programmatic + settings. + + Args: + name (`str`, *optional*): + The group name to check; if None, checks the global setting. + + Returns: + `bool`: True if progress bars are disabled, False otherwise. + """ + if HF_HUB_DISABLE_PROGRESS_BARS is True: + return True + + if name is None: + return not progress_bar_states.get("_global", True) + + while name: + if name in progress_bar_states: + return not progress_bar_states[name] + name = ".".join(name.split(".")[:-1]) + + return not progress_bar_states.get("_global", True) + + +def is_tqdm_disabled(log_level: int) -> Optional[bool]: + """ + Determine if tqdm progress bars should be disabled based on logging level and environment settings. + + see https://github.com/huggingface/huggingface_hub/pull/2000 and https://github.com/huggingface/huggingface_hub/pull/2698. + """ + if log_level == logging.NOTSET: + return True + if os.getenv("TQDM_POSITION") == "-1": + return False + return None + + +class tqdm(old_tqdm): + """ + Class to override `disable` argument in case progress bars are globally disabled. + + Taken from https://github.com/tqdm/tqdm/issues/619#issuecomment-619639324. + """ + + def __init__(self, *args, **kwargs): + name = kwargs.pop("name", None) # do not pass `name` to `tqdm` + if are_progress_bars_disabled(name): + kwargs["disable"] = True + super().__init__(*args, **kwargs) + + def __delattr__(self, attr: str) -> None: + """Fix for https://github.com/huggingface/huggingface_hub/issues/1603""" + try: + super().__delattr__(attr) + except AttributeError: + if attr != "_lock": + raise + + +@contextmanager +def tqdm_stream_file(path: Union[Path, str]) -> Iterator[io.BufferedReader]: + """ + Open a file as binary and wrap the `read` method to display a progress bar when it's streamed. + + First implemented in `transformers` in 2019 but removed when switched to git-lfs. Used in `huggingface_hub` to show + progress bar when uploading an LFS file to the Hub. See github.com/huggingface/transformers/pull/2078#discussion_r354739608 + for implementation details. + + Note: currently implementation handles only files stored on disk as it is the most common use case. Could be + extended to stream any `BinaryIO` object but we might have to debug some corner cases. + + Example: + ```py + >>> with tqdm_stream_file("config.json") as f: + >>> requests.put(url, data=f) + config.json: 100%|█████████████████████████| 8.19k/8.19k [00:02<00:00, 3.72kB/s] + ``` + """ + if isinstance(path, str): + path = Path(path) + + with path.open("rb") as f: + total_size = path.stat().st_size + pbar = tqdm( + unit="B", + unit_scale=True, + total=total_size, + initial=0, + desc=path.name, + ) + + f_read = f.read + + def _inner_read(size: Optional[int] = -1) -> bytes: + data = f_read(size) + pbar.update(len(data)) + return data + + f.read = _inner_read # type: ignore + + yield f + + pbar.close() + + +def _get_progress_bar_context( + *, + desc: str, + log_level: int, + total: Optional[int] = None, + initial: int = 0, + unit: str = "B", + unit_scale: bool = True, + name: Optional[str] = None, + _tqdm_bar: Optional[tqdm] = None, +) -> ContextManager[tqdm]: + if _tqdm_bar is not None: + return nullcontext(_tqdm_bar) + # ^ `contextlib.nullcontext` mimics a context manager that does nothing + # Makes it easier to use the same code path for both cases but in the later + # case, the progress bar is not closed when exiting the context manager. + + return tqdm( + unit=unit, + unit_scale=unit_scale, + total=total, + initial=initial, + desc=desc, + disable=is_tqdm_disabled(log_level=log_level), + name=name, + ) diff --git a/venv/lib/python3.13/site-packages/packaging-25.0.dist-info/licenses/LICENSE b/venv/lib/python3.13/site-packages/packaging-25.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..6f62d44e4ef733c0e713afcd2371fed7f2b3de67 --- /dev/null +++ b/venv/lib/python3.13/site-packages/packaging-25.0.dist-info/licenses/LICENSE @@ -0,0 +1,3 @@ +This software is made available under the terms of *either* of the licenses +found in LICENSE.APACHE or LICENSE.BSD. Contributions to this software is made +under the terms of *both* these licenses. diff --git a/venv/lib/python3.13/site-packages/packaging-25.0.dist-info/licenses/LICENSE.APACHE b/venv/lib/python3.13/site-packages/packaging-25.0.dist-info/licenses/LICENSE.APACHE new file mode 100644 index 0000000000000000000000000000000000000000..f433b1a53f5b830a205fd2df78e2b34974656c7b --- /dev/null +++ b/venv/lib/python3.13/site-packages/packaging-25.0.dist-info/licenses/LICENSE.APACHE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/venv/lib/python3.13/site-packages/packaging-25.0.dist-info/licenses/LICENSE.BSD b/venv/lib/python3.13/site-packages/packaging-25.0.dist-info/licenses/LICENSE.BSD new file mode 100644 index 0000000000000000000000000000000000000000..42ce7b75c92fb01a3f6ed17eea363f756b7da582 --- /dev/null +++ b/venv/lib/python3.13/site-packages/packaging-25.0.dist-info/licenses/LICENSE.BSD @@ -0,0 +1,23 @@ +Copyright (c) Donald Stufft and individual contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/AUTHORS.txt b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/AUTHORS.txt new file mode 100644 index 0000000000000000000000000000000000000000..c8a7c68fa7a3ac18950ac96136ae2e7bf6482707 --- /dev/null +++ b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/AUTHORS.txt @@ -0,0 +1,833 @@ +@Switch01 +A_Rog +Aakanksha Agrawal +Abhinav Sagar +ABHYUDAY PRATAP SINGH +abs51295 +AceGentile +Adam Chainz +Adam Tse +Adam Turner +Adam Wentz +admin +Adolfo Ochagavía +Adrien Morison +Agus +ahayrapetyan +Ahilya +AinsworthK +Akash Srivastava +Alan Yee +Albert Tugushev +Albert-Guan +albertg +Alberto Sottile +Aleks Bunin +Ales Erjavec +Alethea Flowers +Alex Gaynor +Alex Grönholm +Alex Hedges +Alex Loosley +Alex Morega +Alex Stachowiak +Alexander Regueiro +Alexander Shtyrov +Alexandre Conrad +Alexey Popravka +Aleš Erjavec +Alli +Ami Fischman +Ananya Maiti +Anatoly Techtonik +Anders Kaseorg +Andre Aguiar +Andreas Lutro +Andrei Geacar +Andrew Gaul +Andrew Shymanel +Andrey Bienkowski +Andrey Bulgakov +Andrés Delfino +Andy Freeland +Andy Kluger +Ani Hayrapetyan +Aniruddha Basak +Anish Tambe +Anrs Hu +Anthony Sottile +Antoine Musso +Anton Ovchinnikov +Anton Patrushev +Anton Zelenov +Antonio Alvarado Hernandez +Antony Lee +Antti Kaihola +Anubhav Patel +Anudit Nagar +Anuj Godase +AQNOUCH Mohammed +AraHaan +arena +arenasys +Arindam Choudhury +Armin Ronacher +Arnon Yaari +Artem +Arun Babu Neelicattu +Ashley Manton +Ashwin Ramaswami +atse +Atsushi Odagiri +Avinash Karhana +Avner Cohen +Awit (Ah-Wit) Ghirmai +Baptiste Mispelon +Barney Gale +barneygale +Bartek Ogryczak +Bastian Venthur +Ben Bodenmiller +Ben Darnell +Ben Hoyt +Ben Mares +Ben Rosser +Bence Nagy +Benjamin Peterson +Benjamin VanEvery +Benoit Pierre +Berker Peksag +Bernard +Bernard Tyers +Bernardo B. Marques +Bernhard M. Wiedemann +Bertil Hatt +Bhavam Vidyarthi +Blazej Michalik +Bogdan Opanchuk +BorisZZZ +Brad Erickson +Bradley Ayers +Bradley Reynolds +Branch Vincent +Brandon L. Reiss +Brandt Bucher +Brannon Dorsey +Brett Randall +Brett Rosen +Brian Cristante +Brian Rosner +briantracy +BrownTruck +Bruno Oliveira +Bruno Renié +Bruno S +Bstrdsmkr +Buck Golemon +burrows +Bussonnier Matthias +bwoodsend +c22 +Caleb Brown +Caleb Martinez +Calvin Smith +Carl Meyer +Carlos Liam +Carol Willing +Carter Thayer +Cass +Chandrasekhar Atina +Charlie Marsh +charwick +Chih-Hsuan Yen +Chris Brinker +Chris Hunt +Chris Jerdonek +Chris Kuehl +Chris Markiewicz +Chris McDonough +Chris Pawley +Chris Pryer +Chris Wolfe +Christian Clauss +Christian Heimes +Christian Oudard +Christoph Reiter +Christopher Hunt +Christopher Snyder +chrysle +cjc7373 +Clark Boylan +Claudio Jolowicz +Clay McClure +Cody +Cody Soyland +Colin Watson +Collin Anderson +Connor Osborn +Cooper Lees +Cooper Ry Lees +Cory Benfield +Cory Wright +Craig Kerstiens +Cristian Sorinel +Cristina +Cristina Muñoz +ctg123 +Curtis Doty +cytolentino +Daan De Meyer +Dale +Damian +Damian Quiroga +Damian Shaw +Dan Black +Dan Savilonis +Dan Sully +Dane Hillard +daniel +Daniel Collins +Daniel Hahler +Daniel Holth +Daniel Jost +Daniel Katz +Daniel Shaulov +Daniele Esposti +Daniele Nicolodi +Daniele Procida +Daniil Konovalenko +Danny Hermes +Danny McClanahan +Darren Kavanagh +Dav Clark +Dave Abrahams +Dave Jones +David Aguilar +David Black +David Bordeynik +David Caro +David D Lowe +David Evans +David Hewitt +David Linke +David Poggi +David Poznik +David Pursehouse +David Runge +David Tucker +David Wales +Davidovich +ddelange +Deepak Sharma +Deepyaman Datta +Denis Roussel (ACSONE) +Denise Yu +dependabot[bot] +derwolfe +Desetude +developer +Devesh Kumar +Devesh Kumar Singh +devsagul +Diego Caraballo +Diego Ramirez +DiegoCaraballo +Dimitri Merejkowsky +Dimitri Papadopoulos +Dimitri Papadopoulos Orfanos +Dirk Stolle +Dmitry Gladkov +Dmitry Volodin +Domen Kožar +Dominic Davis-Foster +Donald Stufft +Dongweiming +doron zarhi +Dos Moonen +Douglas Thor +DrFeathers +Dustin Ingram +Dustin Rodrigues +Dwayne Bailey +Ed Morley +Edgar Ramírez +Edgar Ramírez Mondragón +Ee Durbin +Efflam Lemaillet +efflamlemaillet +Eitan Adler +ekristina +elainechan +Eli Schwartz +Elisha Hollander +Ellen Marie Dash +Emil Burzo +Emil Styrke +Emmanuel Arias +Endoh Takanao +enoch +Erdinc Mutlu +Eric Cousineau +Eric Gillingham +Eric Hanchrow +Eric Hopper +Erik M. Bray +Erik Rose +Erwin Janssen +Eugene Vereshchagin +everdimension +Federico +Felipe Peter +Felix Yan +fiber-space +Filip Kokosiński +Filipe Laíns +Finn Womack +finnagin +Flavio Amurrio +Florian Briand +Florian Rathgeber +Francesco +Francesco Montesano +Fredrik Orderud +Fredrik Roubert +Frost Ming +Gabriel Curio +Gabriel de Perthuis +Garry Polley +gavin +gdanielson +Gene Wood +Geoffrey Sneddon +George Margaritis +George Song +Georgi Valkov +Georgy Pchelkin +ghost +Giftlin Rajaiah +gizmoguy1 +gkdoc +Godefroid Chapelle +Gopinath M +GOTO Hayato +gousaiyang +gpiks +Greg Roodt +Greg Ward +Guilherme Espada +Guillaume Seguin +gutsytechster +Guy Rozendorn +Guy Tuval +gzpan123 +Hanjun Kim +Hari Charan +Harsh Vardhan +harupy +Harutaka Kawamura +hauntsaninja +Henrich Hartzer +Henry Schreiner +Herbert Pfennig +Holly Stotelmyer +Honnix +Hsiaoming Yang +Hugo Lopes Tavares +Hugo van Kemenade +Hugues Bruant +Hynek Schlawack +iamsrp-deshaw +Ian Bicking +Ian Cordasco +Ian Lee +Ian Stapleton Cordasco +Ian Wienand +Igor Kuzmitshov +Igor Sobreira +Ikko Ashimine +Ilan Schnell +Illia Volochii +Ilya Baryshev +Inada Naoki +Ionel Cristian Mărieș +Ionel Maries Cristian +Itamar Turner-Trauring +iTrooz +Ivan Pozdeev +J. Nick Koston +Jacob Kim +Jacob Walls +Jaime Sanz +Jake Lishman +jakirkham +Jakub Kuczys +Jakub Stasiak +Jakub Vysoky +Jakub Wilk +James Cleveland +James Curtin +James Firth +James Gerity +James Polley +Jan Pokorný +Jannis Leidel +Jarek Potiuk +jarondl +Jason Curtis +Jason R. Coombs +JasonMo +JasonMo1 +Jay Graves +Jean Abou Samra +Jean-Christophe Fillion-Robin +Jeff Barber +Jeff Dairiki +Jeff Widman +Jelmer Vernooij +jenix21 +Jeremy Fleischman +Jeremy Stanley +Jeremy Zafran +Jesse Rittner +Jiashuo Li +Jim Fisher +Jim Garrison +Jinzhe Zeng +Jiun Bae +Jivan Amara +Joa +Joe Bylund +Joe Michelini +Johannes Altmanninger +John Paton +John Sirois +John T. Wodder II +John-Scott Atlakson +johnthagen +Jon Banafato +Jon Dufresne +Jon Parise +Jonas Nockert +Jonathan Herbert +Joonatan Partanen +Joost Molenaar +Jorge Niedbalski +Joseph Bylund +Joseph Long +Josh Bronson +Josh Cannon +Josh Hansen +Josh Schneier +Joshua +JoshuaPerdue +Juan Luis Cano Rodríguez +Juanjo Bazán +Judah Rand +Julian Berman +Julian Gethmann +Julien Demoor +July Tikhonov +Jussi Kukkonen +Justin van Heek +jwg4 +Jyrki Pulliainen +Kai Chen +Kai Mueller +Kamal Bin Mustafa +Karolina Surma +kasium +kaustav haldar +keanemind +Keith Maxwell +Kelsey Hightower +Kenneth Belitzky +Kenneth Reitz +Kevin Burke +Kevin Carter +Kevin Frommelt +Kevin R Patterson +Kexuan Sun +Kit Randel +Klaas van Schelven +KOLANICH +konstin +kpinc +Krishan Bhasin +Krishna Oza +Kumar McMillan +Kuntal Majumder +Kurt McKee +Kyle Persohn +lakshmanaram +Laszlo Kiss-Kollar +Laurent Bristiel +Laurent LAPORTE +Laurie O +Laurie Opperman +layday +Leon Sasson +Lev Givon +Lincoln de Sousa +Lipis +lorddavidiii +Loren Carvalho +Lucas Cimon +Ludovic Gasc +Luis Medel +Lukas Geiger +Lukas Juhrich +Luke Macken +Luo Jiebin +luojiebin +luz.paz +László Kiss Kollár +M00nL1ght +Malcolm Smith +Marc Abramowitz +Marc Tamlyn +Marcus Smith +Mariatta +Mark Kohler +Mark McLoughlin +Mark Williams +Markus Hametner +Martey Dodoo +Martin Fischer +Martin Häcker +Martin Pavlasek +Masaki +Masklinn +Matej Stuchlik +Mathew Jennings +Mathieu Bridon +Mathieu Kniewallner +Matt Bacchi +Matt Good +Matt Maker +Matt Robenolt +Matt Wozniski +matthew +Matthew Einhorn +Matthew Feickert +Matthew Gilliard +Matthew Hughes +Matthew Iversen +Matthew Treinish +Matthew Trumbell +Matthew Willson +Matthias Bussonnier +mattip +Maurits van Rees +Max W Chase +Maxim Kurnikov +Maxime Rouyrre +mayeut +mbaluna +Md Sujauddin Sekh +mdebi +memoselyk +meowmeowcat +Michael +Michael Aquilina +Michael E. Karpeles +Michael Klich +Michael Mintz +Michael Williamson +michaelpacer +Michał Górny +Mickaël Schoentgen +Miguel Araujo Perez +Mihir Singh +Mike +Mike Hendricks +Min RK +MinRK +Miro Hrončok +Monica Baluna +montefra +Monty Taylor +morotti +mrKazzila +Muha Ajjan +Nadav Wexler +Nahuel Ambrosini +Nate Coraor +Nate Prewitt +Nathan Houghton +Nathaniel J. Smith +Nehal J Wani +Neil Botelho +Nguyễn Gia Phong +Nicholas Serra +Nick Coghlan +Nick Stenning +Nick Timkovich +Nicolas Bock +Nicole Harris +Nikhil Benesch +Nikhil Ladha +Nikita Chepanov +Nikolay Korolev +Nipunn Koorapati +Nitesh Sharma +Niyas Sait +Noah +Noah Gorny +Nowell Strite +NtaleGrey +nucccc +nvdv +OBITORASU +Ofek Lev +ofrinevo +Oleg Burnaev +Oliver Freund +Oliver Jeeves +Oliver Mannion +Oliver Tonnhofer +Olivier Girardot +Olivier Grisel +Ollie Rutherfurd +OMOTO Kenji +Omry Yadan +onlinejudge95 +Oren Held +Oscar Benjamin +Oz N Tiram +Pachwenko +Patrick Dubroy +Patrick Jenkins +Patrick Lawson +patricktokeeffe +Patrik Kopkan +Paul Ganssle +Paul Kehrer +Paul Moore +Paul Nasrat +Paul Oswald +Paul van der Linden +Paulus Schoutsen +Pavel Safronov +Pavithra Eswaramoorthy +Pawel Jasinski +Paweł Szramowski +Pekka Klärck +Peter Gessler +Peter Lisák +Peter Shen +Peter Waller +Petr Viktorin +petr-tik +Phaneendra Chiruvella +Phil Elson +Phil Freo +Phil Pennock +Phil Whelan +Philip Jägenstedt +Philip Molloy +Philippe Ombredanne +Pi Delport +Pierre-Yves Rofes +Pieter Degroote +pip +Prabakaran Kumaresshan +Prabhjyotsing Surjit Singh Sodhi +Prabhu Marappan +Pradyun Gedam +Prashant Sharma +Pratik Mallya +pre-commit-ci[bot] +Preet Thakkar +Preston Holmes +Przemek Wrzos +Pulkit Goyal +q0w +Qiangning Hong +Qiming Xu +qraqras +Quentin Lee +Quentin Pradet +R. David Murray +Rafael Caricio +Ralf Schmitt +Ran Benita +Randy Döring +Razzi Abuissa +rdb +Reece Dunham +Remi Rampin +Rene Dudfield +Riccardo Magliocchetti +Riccardo Schirone +Richard Jones +Richard Si +Ricky Ng-Adam +Rishi +rmorotti +RobberPhex +Robert Collins +Robert McGibbon +Robert Pollak +Robert T. McGibbon +robin elisha robinson +Rodney, Tiara +Roey Berman +Rohan Jain +Roman Bogorodskiy +Roman Donchenko +Romuald Brunet +ronaudinho +Ronny Pfannschmidt +Rory McCann +Ross Brattain +Roy Wellington Ⅳ +Ruairidh MacLeod +Russell Keith-Magee +Ryan Shepherd +Ryan Wooden +ryneeverett +Ryuma Asai +S. Guliaev +Sachi King +Salvatore Rinchiera +sandeepkiran-js +Sander Van Balen +Savio Jomton +schlamar +Scott Kitterman +Sean +seanj +Sebastian Jordan +Sebastian Schaetz +Segev Finer +SeongSoo Cho +Sepehr Rasouli +sepehrrasooli +Sergey Vasilyev +Seth Michael Larson +Seth Woodworth +Shahar Epstein +Shantanu +shenxianpeng +shireenrao +Shivansh-007 +Shixian Sheng +Shlomi Fish +Shovan Maity +Simeon Visser +Simon Cross +Simon Pichugin +sinoroc +sinscary +snook92 +socketubs +Sorin Sbarnea +Srinivas Nyayapati +Srishti Hegde +Stavros Korokithakis +Stefan Scherfke +Stefano Rivera +Stephan Erb +Stephen Payne +Stephen Rosen +stepshal +Steve (Gadget) Barnes +Steve Barnes +Steve Dower +Steve Kowalik +Steven Myint +Steven Silvester +stonebig +studioj +Stéphane Bidoul +Stéphane Bidoul (ACSONE) +Stéphane Klein +Sumana Harihareswara +Surbhi Sharma +Sviatoslav Sydorenko +Sviatoslav Sydorenko (Святослав Сидоренко) +Swat009 +Sylvain +Takayuki SHIMIZUKAWA +Taneli Hukkinen +tbeswick +Thiago +Thijs Triemstra +Thomas Fenzl +Thomas Grainger +Thomas Guettler +Thomas Johansson +Thomas Kluyver +Thomas Smith +Thomas VINCENT +Tim D. Smith +Tim Gates +Tim Harder +Tim Heap +tim smith +tinruufu +Tobias Hermann +Tom Forbes +Tom Freudenheim +Tom V +Tomas Hrnciar +Tomas Orsava +Tomer Chachamu +Tommi Enenkel | AnB +Tomáš Hrnčiar +Tony Beswick +Tony Narlock +Tony Zhaocheng Tan +TonyBeswick +toonarmycaptain +Toshio Kuratomi +toxinu +Travis Swicegood +Tushar Sadhwani +Tzu-ping Chung +Valentin Haenel +Victor Stinner +victorvpaulo +Vikram - Google +Viktor Szépe +Ville Skyttä +Vinay Sajip +Vincent Philippon +Vinicyus Macedo +Vipul Kumar +Vitaly Babiy +Vladimir Fokow +Vladimir Rutsky +W. Trevor King +Wil Tan +Wilfred Hughes +William Edwards +William ML Leslie +William T Olson +William Woodruff +Wilson Mo +wim glenn +Winson Luk +Wolfgang Maier +Wu Zhenyu +XAMES3 +Xavier Fernandez +Xianpeng Shen +xoviat +xtreak +YAMAMOTO Takashi +Yen Chi Hsuan +Yeray Diaz Diaz +Yoval P +Yu Jian +Yuan Jing Vincent Yan +Yuki Kobayashi +Yusuke Hayashi +zackzack38 +Zearin +Zhiping Deng +ziebam +Zvezdan Petkovic +Łukasz Langa +Роман Донченко +Семён Марьясин diff --git a/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/LICENSE.txt b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..8e7b65eaf628360e6f32f4140fcdd7ec7c2b7077 --- /dev/null +++ b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) 2008-present The pip developers (see AUTHORS.txt file) + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..d8b3b56d361cfaa04de6304c96c328f27266f74e --- /dev/null +++ b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/cachecontrol/LICENSE.txt @@ -0,0 +1,13 @@ +Copyright 2012-2021 Eric Larson + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/certifi/LICENSE b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/certifi/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..62b076cdee58ec8f34034141ba0befd9015b0c7e --- /dev/null +++ b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/certifi/LICENSE @@ -0,0 +1,20 @@ +This package contains a modified version of ca-bundle.crt: + +ca-bundle.crt -- Bundle of CA Root Certificates + +This is a bundle of X.509 certificates of public Certificate Authorities +(CA). These were automatically extracted from Mozilla's root certificates +file (certdata.txt). This file can be found in the mozilla source tree: +https://hg.mozilla.org/mozilla-central/file/tip/security/nss/lib/ckfw/builtins/certdata.txt +It contains the certificates in PEM format and therefore +can be directly used with curl / libcurl / php_curl, or with +an Apache+mod_ssl webserver for SSL client authentication. +Just configure this file as the SSLCACertificateFile.# + +***** BEGIN LICENSE BLOCK ***** +This Source Code Form is subject to the terms of the Mozilla Public License, +v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain +one at http://mozilla.org/MPL/2.0/. + +***** END LICENSE BLOCK ***** +@(#) $RCSfile: certdata.txt,v $ $Revision: 1.80 $ $Date: 2011/11/03 15:11:58 $ diff --git a/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/dependency_groups/LICENSE.txt b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/dependency_groups/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..b9723b85ed7adb32f8c2b4ee880c06ec3061da50 --- /dev/null +++ b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/dependency_groups/LICENSE.txt @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2024-present Stephen Rosen + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/distlib/LICENSE.txt b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/distlib/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..c31ac56d772f7061c8b42f0ee96063b7a94a4859 --- /dev/null +++ b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/distlib/LICENSE.txt @@ -0,0 +1,284 @@ +A. HISTORY OF THE SOFTWARE +========================== + +Python was created in the early 1990s by Guido van Rossum at Stichting +Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands +as a successor of a language called ABC. Guido remains Python's +principal author, although it includes many contributions from others. + +In 1995, Guido continued his work on Python at the Corporation for +National Research Initiatives (CNRI, see http://www.cnri.reston.va.us) +in Reston, Virginia where he released several versions of the +software. + +In May 2000, Guido and the Python core development team moved to +BeOpen.com to form the BeOpen PythonLabs team. In October of the same +year, the PythonLabs team moved to Digital Creations (now Zope +Corporation, see http://www.zope.com). In 2001, the Python Software +Foundation (PSF, see http://www.python.org/psf/) was formed, a +non-profit organization created specifically to own Python-related +Intellectual Property. Zope Corporation is a sponsoring member of +the PSF. + +All Python releases are Open Source (see http://www.opensource.org for +the Open Source Definition). Historically, most, but not all, Python +releases have also been GPL-compatible; the table below summarizes +the various releases. + + Release Derived Year Owner GPL- + from compatible? (1) + + 0.9.0 thru 1.2 1991-1995 CWI yes + 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes + 1.6 1.5.2 2000 CNRI no + 2.0 1.6 2000 BeOpen.com no + 1.6.1 1.6 2001 CNRI yes (2) + 2.1 2.0+1.6.1 2001 PSF no + 2.0.1 2.0+1.6.1 2001 PSF yes + 2.1.1 2.1+2.0.1 2001 PSF yes + 2.2 2.1.1 2001 PSF yes + 2.1.2 2.1.1 2002 PSF yes + 2.1.3 2.1.2 2002 PSF yes + 2.2.1 2.2 2002 PSF yes + 2.2.2 2.2.1 2002 PSF yes + 2.2.3 2.2.2 2003 PSF yes + 2.3 2.2.2 2002-2003 PSF yes + 2.3.1 2.3 2002-2003 PSF yes + 2.3.2 2.3.1 2002-2003 PSF yes + 2.3.3 2.3.2 2002-2003 PSF yes + 2.3.4 2.3.3 2004 PSF yes + 2.3.5 2.3.4 2005 PSF yes + 2.4 2.3 2004 PSF yes + 2.4.1 2.4 2005 PSF yes + 2.4.2 2.4.1 2005 PSF yes + 2.4.3 2.4.2 2006 PSF yes + 2.4.4 2.4.3 2006 PSF yes + 2.5 2.4 2006 PSF yes + 2.5.1 2.5 2007 PSF yes + 2.5.2 2.5.1 2008 PSF yes + 2.5.3 2.5.2 2008 PSF yes + 2.6 2.5 2008 PSF yes + 2.6.1 2.6 2008 PSF yes + 2.6.2 2.6.1 2009 PSF yes + 2.6.3 2.6.2 2009 PSF yes + 2.6.4 2.6.3 2009 PSF yes + 2.6.5 2.6.4 2010 PSF yes + 3.0 2.6 2008 PSF yes + 3.0.1 3.0 2009 PSF yes + 3.1 3.0.1 2009 PSF yes + 3.1.1 3.1 2009 PSF yes + 3.1.2 3.1 2010 PSF yes + 3.2 3.1 2010 PSF yes + +Footnotes: + +(1) GPL-compatible doesn't mean that we're distributing Python under + the GPL. All Python licenses, unlike the GPL, let you distribute + a modified version without making your changes open source. The + GPL-compatible licenses make it possible to combine Python with + other software that is released under the GPL; the others don't. + +(2) According to Richard Stallman, 1.6.1 is not GPL-compatible, + because its license has a choice of law clause. According to + CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 + is "not incompatible" with the GPL. + +Thanks to the many outside volunteers who have worked under Guido's +direction to make these releases possible. + + +B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON +=============================================================== + +PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 +-------------------------------------------- + +1. This LICENSE AGREEMENT is between the Python Software Foundation +("PSF"), and the Individual or Organization ("Licensee") accessing and +otherwise using this software ("Python") in source or binary form and +its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby +grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, +analyze, test, perform and/or display publicly, prepare derivative works, +distribute, and otherwise use Python alone or in any derivative version, +provided, however, that PSF's License Agreement and PSF's notice of copyright, +i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 +Python Software Foundation; All Rights Reserved" are retained in Python alone or +in any derivative version prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python. + +4. PSF is making Python available to Licensee on an "AS IS" +basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between PSF and +Licensee. This License Agreement does not grant permission to use PSF +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Python, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 +------------------------------------------- + +BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 + +1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an +office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the +Individual or Organization ("Licensee") accessing and otherwise using +this software in source or binary form and its associated +documentation ("the Software"). + +2. Subject to the terms and conditions of this BeOpen Python License +Agreement, BeOpen hereby grants Licensee a non-exclusive, +royalty-free, world-wide license to reproduce, analyze, test, perform +and/or display publicly, prepare derivative works, distribute, and +otherwise use the Software alone or in any derivative version, +provided, however, that the BeOpen Python License is retained in the +Software, alone or in any derivative version prepared by Licensee. + +3. BeOpen is making the Software available to Licensee on an "AS IS" +basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE +SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS +AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY +DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +5. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +6. This License Agreement shall be governed by and interpreted in all +respects by the law of the State of California, excluding conflict of +law provisions. Nothing in this License Agreement shall be deemed to +create any relationship of agency, partnership, or joint venture +between BeOpen and Licensee. This License Agreement does not grant +permission to use BeOpen trademarks or trade names in a trademark +sense to endorse or promote products or services of Licensee, or any +third party. As an exception, the "BeOpen Python" logos available at +http://www.pythonlabs.com/logos.html may be used according to the +permissions granted on that web page. + +7. By copying, installing or otherwise using the software, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 +--------------------------------------- + +1. This LICENSE AGREEMENT is between the Corporation for National +Research Initiatives, having an office at 1895 Preston White Drive, +Reston, VA 20191 ("CNRI"), and the Individual or Organization +("Licensee") accessing and otherwise using Python 1.6.1 software in +source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, CNRI +hereby grants Licensee a nonexclusive, royalty-free, world-wide +license to reproduce, analyze, test, perform and/or display publicly, +prepare derivative works, distribute, and otherwise use Python 1.6.1 +alone or in any derivative version, provided, however, that CNRI's +License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) +1995-2001 Corporation for National Research Initiatives; All Rights +Reserved" are retained in Python 1.6.1 alone or in any derivative +version prepared by Licensee. Alternately, in lieu of CNRI's License +Agreement, Licensee may substitute the following text (omitting the +quotes): "Python 1.6.1 is made available subject to the terms and +conditions in CNRI's License Agreement. This Agreement together with +Python 1.6.1 may be located on the Internet using the following +unique, persistent identifier (known as a handle): 1895.22/1013. This +Agreement may also be obtained from a proxy server on the Internet +using the following URL: http://hdl.handle.net/1895.22/1013". + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python 1.6.1 or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python 1.6.1. + +4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" +basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. This License Agreement shall be governed by the federal +intellectual property law of the United States, including without +limitation the federal copyright law, and, to the extent such +U.S. federal law does not apply, by the law of the Commonwealth of +Virginia, excluding Virginia's conflict of law provisions. +Notwithstanding the foregoing, with regard to derivative works based +on Python 1.6.1 that incorporate non-separable material that was +previously distributed under the GNU General Public License (GPL), the +law of the Commonwealth of Virginia shall govern this License +Agreement only as to issues arising under or with respect to +Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this +License Agreement shall be deemed to create any relationship of +agency, partnership, or joint venture between CNRI and Licensee. This +License Agreement does not grant permission to use CNRI trademarks or +trade name in a trademark sense to endorse or promote products or +services of Licensee, or any third party. + +8. By clicking on the "ACCEPT" button where indicated, or by copying, +installing or otherwise using Python 1.6.1, Licensee agrees to be +bound by the terms and conditions of this License Agreement. + + ACCEPT + + +CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 +-------------------------------------------------- + +Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, +The Netherlands. All rights reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/distro/LICENSE b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/distro/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..e06d2081865a766a8668acc12878f98b27fc9ea0 --- /dev/null +++ b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/distro/LICENSE @@ -0,0 +1,202 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/idna/LICENSE.md b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/idna/LICENSE.md new file mode 100644 index 0000000000000000000000000000000000000000..19b6b45242c16a1025465309eec2ca5009319de3 --- /dev/null +++ b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/idna/LICENSE.md @@ -0,0 +1,31 @@ +BSD 3-Clause License + +Copyright (c) 2013-2024, Kim Davies and contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/msgpack/COPYING b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/msgpack/COPYING new file mode 100644 index 0000000000000000000000000000000000000000..f067af3aae15a5b494f0eeb15a7064c176e30fd5 --- /dev/null +++ b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/msgpack/COPYING @@ -0,0 +1,14 @@ +Copyright (C) 2008-2011 INADA Naoki + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/packaging/LICENSE b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/packaging/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..6f62d44e4ef733c0e713afcd2371fed7f2b3de67 --- /dev/null +++ b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/packaging/LICENSE @@ -0,0 +1,3 @@ +This software is made available under the terms of *either* of the licenses +found in LICENSE.APACHE or LICENSE.BSD. Contributions to this software is made +under the terms of *both* these licenses. diff --git a/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.APACHE b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.APACHE new file mode 100644 index 0000000000000000000000000000000000000000..f433b1a53f5b830a205fd2df78e2b34974656c7b --- /dev/null +++ b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.APACHE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.BSD b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.BSD new file mode 100644 index 0000000000000000000000000000000000000000..42ce7b75c92fb01a3f6ed17eea363f756b7da582 --- /dev/null +++ b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/packaging/LICENSE.BSD @@ -0,0 +1,23 @@ +Copyright (c) Donald Stufft and individual contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/pkg_resources/LICENSE b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/pkg_resources/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..1bb5a44356f00884a71ceeefd24ded6caaba2418 --- /dev/null +++ b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/pkg_resources/LICENSE @@ -0,0 +1,17 @@ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/platformdirs/LICENSE b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/platformdirs/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..f35fed9191b1142ddaada8a96de4a9461c5d796c --- /dev/null +++ b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/platformdirs/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2010-202x The platformdirs developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/pygments/LICENSE b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/pygments/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..446a1a805c8a949579fb8a9799f2bec7777349dc --- /dev/null +++ b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/pygments/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2006-2022 by the respective authors (see AUTHORS file). +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/pyproject_hooks/LICENSE b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/pyproject_hooks/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..b0ae9dbc262b5f7ed4cb6f964cfb5eb5c0d0b0be --- /dev/null +++ b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/pyproject_hooks/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Thomas Kluyver + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/requests/LICENSE b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/requests/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..67db8588217f266eb561f75fae738656325deac9 --- /dev/null +++ b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/requests/LICENSE @@ -0,0 +1,175 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/resolvelib/LICENSE b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/resolvelib/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..b9077766e9b9bdcae49ea5c8fced750ed13ec8f7 --- /dev/null +++ b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/resolvelib/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2018, Tzu-ping Chung + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/rich/LICENSE b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/rich/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..4415505566f261c802b671426be529a31f914137 --- /dev/null +++ b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/rich/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2020 Will McGugan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/tomli/LICENSE b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/tomli/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..e859590f886cd78344206af1a8ccb3080d4385e0 --- /dev/null +++ b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/tomli/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Taneli Hukkinen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/tomli/LICENSE-HEADER b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/tomli/LICENSE-HEADER new file mode 100644 index 0000000000000000000000000000000000000000..aba78dd2cfc42c15dc73d0e8841245bd4df89d31 --- /dev/null +++ b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/tomli/LICENSE-HEADER @@ -0,0 +1,3 @@ +SPDX-License-Identifier: MIT +SPDX-FileCopyrightText: 2021 Taneli Hukkinen +Licensed to PSF under a Contributor Agreement. diff --git a/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/tomli_w/LICENSE b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/tomli_w/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..e859590f886cd78344206af1a8ccb3080d4385e0 --- /dev/null +++ b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/tomli_w/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Taneli Hukkinen + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/truststore/LICENSE b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/truststore/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..7ec568c1136eb8ec86aa05cf7abacfb54c3e5ead --- /dev/null +++ b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/truststore/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2022 Seth Michael Larson + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/urllib3/LICENSE.txt b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/urllib3/LICENSE.txt new file mode 100644 index 0000000000000000000000000000000000000000..429a1767e4476707d2e45ee499e3c834ca4b187f --- /dev/null +++ b/venv/lib/python3.13/site-packages/pip-25.2.dist-info/licenses/src/pip/_vendor/urllib3/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2008-2020 Andrey Petrov and contributors (see CONTRIBUTORS.txt) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/venv/lib/python3.13/site-packages/pyyaml-6.0.3.dist-info/licenses/LICENSE b/venv/lib/python3.13/site-packages/pyyaml-6.0.3.dist-info/licenses/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..2f1b8e15e5627d92f0521605c9870bc8e5505cb4 --- /dev/null +++ b/venv/lib/python3.13/site-packages/pyyaml-6.0.3.dist-info/licenses/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2017-2021 Ingy döt Net +Copyright (c) 2006-2016 Kirill Simonov + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/venv/lib/python3.13/site-packages/requests/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/requests/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a0b170fc12df79ad931536ed652b755c1c682299 Binary files /dev/null and b/venv/lib/python3.13/site-packages/requests/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/requests/__pycache__/__version__.cpython-313.pyc b/venv/lib/python3.13/site-packages/requests/__pycache__/__version__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4085aab30f19df9019ce0354adf1ce0f1857608b Binary files /dev/null and b/venv/lib/python3.13/site-packages/requests/__pycache__/__version__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/requests/__pycache__/_internal_utils.cpython-313.pyc b/venv/lib/python3.13/site-packages/requests/__pycache__/_internal_utils.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..de727684caa35242631931e39b74d690b9d7cd82 Binary files /dev/null and b/venv/lib/python3.13/site-packages/requests/__pycache__/_internal_utils.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/requests/__pycache__/adapters.cpython-313.pyc b/venv/lib/python3.13/site-packages/requests/__pycache__/adapters.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a01f7e6e67bee35de410544b653fc55d80fa2986 Binary files /dev/null and b/venv/lib/python3.13/site-packages/requests/__pycache__/adapters.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/requests/__pycache__/api.cpython-313.pyc b/venv/lib/python3.13/site-packages/requests/__pycache__/api.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1f3373cc39292b4b37bd7584efa649df103f2c50 Binary files /dev/null and b/venv/lib/python3.13/site-packages/requests/__pycache__/api.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/requests/__pycache__/auth.cpython-313.pyc b/venv/lib/python3.13/site-packages/requests/__pycache__/auth.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6672fdcc8f554e736e6213d494e1ce0d96514914 Binary files /dev/null and b/venv/lib/python3.13/site-packages/requests/__pycache__/auth.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/requests/__pycache__/certs.cpython-313.pyc b/venv/lib/python3.13/site-packages/requests/__pycache__/certs.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eb659478cf3b85fadf8709bd6b1bf0ce919fc356 Binary files /dev/null and b/venv/lib/python3.13/site-packages/requests/__pycache__/certs.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/requests/__pycache__/compat.cpython-313.pyc b/venv/lib/python3.13/site-packages/requests/__pycache__/compat.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d12a3875b5fbf2b0beb2748c6d71f94075a79cd8 Binary files /dev/null and b/venv/lib/python3.13/site-packages/requests/__pycache__/compat.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/requests/__pycache__/cookies.cpython-313.pyc b/venv/lib/python3.13/site-packages/requests/__pycache__/cookies.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f52281b3b740139c51f3e712fb2e993d1fa82e98 Binary files /dev/null and b/venv/lib/python3.13/site-packages/requests/__pycache__/cookies.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/requests/__pycache__/exceptions.cpython-313.pyc b/venv/lib/python3.13/site-packages/requests/__pycache__/exceptions.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..901c4cead1d1fa373c84f7cf4ece1d77279c13e5 Binary files /dev/null and b/venv/lib/python3.13/site-packages/requests/__pycache__/exceptions.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/requests/__pycache__/help.cpython-313.pyc b/venv/lib/python3.13/site-packages/requests/__pycache__/help.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e9b872a55626a070a160c8d8387f979867fc6c92 Binary files /dev/null and b/venv/lib/python3.13/site-packages/requests/__pycache__/help.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/requests/__pycache__/hooks.cpython-313.pyc b/venv/lib/python3.13/site-packages/requests/__pycache__/hooks.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e60d250a4bde85b5211b32413e4b5e0059c33362 Binary files /dev/null and b/venv/lib/python3.13/site-packages/requests/__pycache__/hooks.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/requests/__pycache__/models.cpython-313.pyc b/venv/lib/python3.13/site-packages/requests/__pycache__/models.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8a95a7e2434fff7b525d5bea150d57d15aff8b7d Binary files /dev/null and b/venv/lib/python3.13/site-packages/requests/__pycache__/models.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/requests/__pycache__/packages.cpython-313.pyc b/venv/lib/python3.13/site-packages/requests/__pycache__/packages.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5fc1bdf7bafe6c913fe0bf6eaae13e99dba8686a Binary files /dev/null and b/venv/lib/python3.13/site-packages/requests/__pycache__/packages.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/requests/__pycache__/sessions.cpython-313.pyc b/venv/lib/python3.13/site-packages/requests/__pycache__/sessions.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..677be76b930f9638fcebb03b86bf7fb2f80dd140 Binary files /dev/null and b/venv/lib/python3.13/site-packages/requests/__pycache__/sessions.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/requests/__pycache__/status_codes.cpython-313.pyc b/venv/lib/python3.13/site-packages/requests/__pycache__/status_codes.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..281426c76c294c8f4288c18b5c75eef880798f2d Binary files /dev/null and b/venv/lib/python3.13/site-packages/requests/__pycache__/status_codes.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/requests/__pycache__/structures.cpython-313.pyc b/venv/lib/python3.13/site-packages/requests/__pycache__/structures.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..75f9eb1138e37e1591b65ef32e9b6d659f6c3576 Binary files /dev/null and b/venv/lib/python3.13/site-packages/requests/__pycache__/structures.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/requests/__pycache__/utils.cpython-313.pyc b/venv/lib/python3.13/site-packages/requests/__pycache__/utils.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4d2e47f67ead79ebde3739d618b35fbe67b6decf Binary files /dev/null and b/venv/lib/python3.13/site-packages/requests/__pycache__/utils.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/safetensors-0.7.0.dist-info/licenses/LICENSE b/venv/lib/python3.13/site-packages/safetensors-0.7.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64 --- /dev/null +++ b/venv/lib/python3.13/site-packages/safetensors-0.7.0.dist-info/licenses/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/venv/lib/python3.13/site-packages/transformers-4.57.2.dist-info/licenses/LICENSE b/venv/lib/python3.13/site-packages/transformers-4.57.2.dist-info/licenses/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..68b7d66c97d66c58de883ed0c451af2b3183e6f3 --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers-4.57.2.dist-info/licenses/LICENSE @@ -0,0 +1,203 @@ +Copyright 2018- The Hugging Face team. All rights reserved. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/venv/lib/python3.13/site-packages/transformers/distributed/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/distributed/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c686132f813ca6a116f332f9b3a9f0384f5620e5 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/distributed/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/distributed/__pycache__/configuration_utils.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/distributed/__pycache__/configuration_utils.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ceb89a1bb92dc617b9faa08460c255b2699f1243 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/distributed/__pycache__/configuration_utils.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8b2219e8654082f1ca2ae7985048958b3bb7d002 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/beam_constraints.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/beam_constraints.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f544fdf1b056d08f7b97c4d019c7cd8cbd52955c Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/beam_constraints.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/beam_search.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/beam_search.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9a9ddce8b320259a76e29448b38a0f1495b98214 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/beam_search.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/candidate_generator.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/candidate_generator.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d979796165cb53274a8fe0453575706b66265e09 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/candidate_generator.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/configuration_utils.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/configuration_utils.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..11361f880c7b3dcde23d945e642cacb68f2197a8 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/configuration_utils.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/flax_logits_process.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/flax_logits_process.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..534eef4dad188c37cbeb1f6a9ead8b89a24c8918 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/flax_logits_process.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/flax_utils.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/flax_utils.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9075b5168521e813ed496c58d8e84c27925448c5 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/flax_utils.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/stopping_criteria.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/stopping_criteria.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..09ec31a2d9f1df2fbaeadec5ae1d3a993f0479a7 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/stopping_criteria.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/streamers.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/streamers.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ba7376210e768f9fd622cde4eb1ef9607c44b225 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/streamers.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/tf_logits_process.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/tf_logits_process.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..03cbbb378b2574d0040ecfaf96352f4f7d00845a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/tf_logits_process.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/watermarking.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/watermarking.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3f326a8252bdd64172a9e53b7d3b7fcfed2cb8bd Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/generation/__pycache__/watermarking.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/__init__.py b/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..8d6800f7db3550fbadfafcac6e2ae0c81eae53d7 --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/__init__.py @@ -0,0 +1,26 @@ +# coding=utf-8 +# Copyright 2025 The HuggingFace Inc. team +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from .cache import PagedAttentionCache +from .continuous_api import ContinuousBatchingManager, ContinuousMixin +from .requests import RequestState, RequestStatus + + +__all__ = [ + "ContinuousBatchingManager", + "ContinuousMixin", + "PagedAttentionCache", + "RequestState", + "RequestStatus", +] diff --git a/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ae1be20587db8a0104ee539fd34e62036730c843 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/__pycache__/cache.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/__pycache__/cache.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..27dbef7e86afa01dbc11443b56155f964a7bb1d5 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/__pycache__/cache.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/__pycache__/cache_manager.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/__pycache__/cache_manager.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9cb51425ed11dff89f123a054b6226e1ff728f30 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/__pycache__/cache_manager.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/__pycache__/continuous_api.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/__pycache__/continuous_api.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f7c85ba3d63889bb82ea0bcfb9b75122ba3a2448 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/__pycache__/continuous_api.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/__pycache__/requests.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/__pycache__/requests.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0484097e84b9f9e133ef617fd58743657061b6d2 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/__pycache__/requests.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/__pycache__/scheduler.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/__pycache__/scheduler.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c50deb3fbb8cdf0f098d6642a6e3cef0a69aea5b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/__pycache__/scheduler.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/cache.py b/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/cache.py new file mode 100644 index 0000000000000000000000000000000000000000..8d6e057be84a7f04f3638bad859af47910ba5ca9 --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/cache.py @@ -0,0 +1,606 @@ +# coding=utf-8 +# Copyright 2025 The HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from collections import deque +from math import floor, gcd, sqrt +from typing import Optional, Union + +import torch + +from ...configuration_utils import PretrainedConfig +from ...generation.configuration_utils import GenerationConfig +from ...utils.metrics import attach_tracer, traced +from .cache_manager import CacheAllocator, FullAttentionCacheAllocator, SlidingAttentionCacheAllocator +from .requests import get_device_and_memory_breakdown, logger + + +def group_layers_by_attn_type(config: PretrainedConfig) -> tuple[list[list[int]], list[str]]: + """ + Group layers depending on the attention mix, according to VLLM's hybrid allocator rules: + - Layers in each group need to have the same type of attention + - All groups have the same number of layers + + For a model with the following layer types: ["sliding", "full", "full", "sliding", "full", "full", "full", "full"] + We would get two groups: [0, 3] and [1, 2], [4,5], [6,7]. + """ + # If the config has no layer_type attribute, it means all layers are the same attention type + layer_types = getattr(config, "layer_types", None) + if layer_types is None: + attn_type = "sliding_attention" if getattr(config, "sliding_window", None) is not None else "full_attention" + layer_types = [attn_type for _ in range(config.num_hidden_layers)] + + # We then count the number of layers of each type + layer_counts = {} + for i, layer_type in enumerate(layer_types): + layer_counts[layer_type] = layer_counts.get(layer_type, []) + [i] + + # The size of all groups is the greatest common divisor of the number of layers of each type + group_size = gcd(*[len(indices) for indices in layer_counts.values()]) + + # We then group the layers by type + layer_groups = [] + for layer_type, indices in layer_counts.items(): + for i in range(0, len(indices), group_size): + layer_groups.append(indices[i : i + group_size]) + # And note the layer types + group_types = [layer_types[lg[0]] for lg in layer_groups] + return layer_groups, group_types + + +@attach_tracer() +class PagedAttentionCache: + """ + Manages the cache for a paged attention mechanism, inspired by VLLM's hybrid allocator. The cache relies on making + groups of layers to reduce the complexity of cache management and fragmentation. + + The cache uses a three-level hierarchy: + - Pages: The smallest unit of cache, a page has a size of [num_heads, head_size], which is the space needed to + store the key or value states for one token and one layer. For a model with only full-attention layers, to store + the KV cache of one token, we need `2 * num_layers` pages: key and values each take `num_layers` pages. + Pages are grouped into blocks: + - Blocks: A block is a collection of `block_size` pages, serving as the allocation unit to reduce management + complexity and fragmentation. Cache is allocated and freed block by block, not page by page. One block is + allocated to one layer group, which only has one attention type, like full-attention or sliding-attention. + If all layers in the model have the same attention type, then all layers will be in the same group. There is + more than one group if and only if the model has a mixed attention types, like layers with full-attention and + layers with sliding-attention. + - Cache tensors: The physical supports for the cache. There are as many cache tensors as there are layer in a + layer group, and the shape of the cache tensor is `[num_blocks * block_size, num_heads, head_size]`. + + Grouping layers into groups is useful because when we allocate one block to a group N, the block allocated is the + same for all layers in group N, equivalently it is allocated across all cache tensors. This allows us to + efficiently allocate and free blocks, and to efficiently read and write key and value states. + + For instance, imagine we have 8 blocks of cache and a model with two layer groups: a full-attention group with 3 + layers and a sliding-attention group with 3 layers. At creation time, the physical cache tensors look like this: + + cache_tensor_0: □ □ □ □ □ □ □ □ + cache_tensor_1: □ □ □ □ □ □ □ □ + cache_tensor_2: □ □ □ □ □ □ □ □ + + where □ means the blocks is not allocated to any layer group yet. We have 3 cache tensors because there are + 3 layers per group. + We allocate 1 block to each group, after allocation, the cache tensors look like this: + + cache_tensor_0: ✖ ◉ □ □ □ □ □ □ + cache_tensor_1: ✖ ◉ □ □ □ □ □ □ + cache_tensor_2: ✖ ◉ □ □ □ □ □ □ + + where ✖ means the block is allocated to the full-attention group, and ◉ means the block is allocated to the + sliding-attention group. + Now, if we continue to generate, and the sliding window has been reached, we only need to allocate a new block + for the full-attention group, and the cache tensors look like this: + + cache_tensor_0: ✖ ◉ ✖ □ □ □ □ □ + cache_tensor_1: ✖ ◉ ✖ □ □ □ □ □ + cache_tensor_2: ✖ ◉ ✖ □ □ □ □ □ + + And after further generation, when we need a new block allocated: + + cache_tensor_0: ✖ ◉ ✖ ✖ □ □ □ □ + cache_tensor_1: ✖ ◉ ✖ ✖ □ □ □ □ + cache_tensor_2: ✖ ◉ ✖ ✖ □ □ □ □ + + This would not have been possible if all layers were in the same group: we would have had to allocate a new block + for the sliding-attention group, although it is not needed. + """ + + # TODO: this init is quite long, maybe a refactor is in order + def __init__( + self, + config: PretrainedConfig, + generation_config: GenerationConfig, + device: torch.device, + dtype: torch.dtype = torch.float16, + layer_device_map: Optional[dict[int, Union[str, torch.device, int]]] = None, + tp_size: Optional[int] = None, + ) -> None: + """Initialize a paged attention cache for efficient memory usage. + + Args: + config: Model configuration + generation_config: Generation configuration containing cache parameters + device: Device for the cache tensors + dtype: Data type of the cache + layer_device_map: Optional mapping of layer indices to devices + tp_size: Tensor parallelism size + """ + self.config = config + self.dtype = dtype + self.device = device + + # Extract model dimensions + kv_heads = getattr(config, "num_key_value_heads", None) + self.num_key_value_heads: int = kv_heads if kv_heads is not None else config.num_attention_heads + head_dim = getattr(config, "head_dim", None) + self.head_dim: int = head_dim if head_dim is not None else config.hidden_size // config.num_attention_heads + + # Extract cache dimensions + self.block_size = getattr(generation_config, "block_size", 32) + + # Group layers depending on the attention mix + layer_groups, group_types = group_layers_by_attn_type(config) + group_size = len(layer_groups[0]) + self.num_groups = len(layer_groups) + + self.sliding_windows = {} + self.layer_index_to_group_indices = {} + for i, group in enumerate(layer_groups): + sliding_window = config.sliding_window if group_types[i] == "sliding_attention" else 1 + for j, layer in enumerate(group): + self.layer_index_to_group_indices[layer] = (i, j) + self.sliding_windows[layer] = sliding_window + + # Handle TP (or dont) + if tp_size is not None and tp_size > 1: + if self.num_key_value_heads % tp_size != 0: + raise ValueError( + f"Number of key value heads {self.num_key_value_heads} must be divisible by tensor parallel size {tp_size}." + ) + # If the model is using tensor parallelism, we need to adjust the number of heads accordingly. + # self.num_key_value_heads //= tp_size # TODO: why is this commented out? + + # Infer number of blocks and max batch tokens + page_size = self.head_dim * self.num_key_value_heads + + if getattr(config, "attn_implementation", None) == "paged_attention": + num_attention_masks = 0 + else: + # TODO: when we generalize to allow for block-attn, we can use `num_attention_masks=sum(set(group_types))` + num_attention_masks = 2 if "sliding_attention" in group_types else 1 + + memory_handler = PagedAttentionMemoryHandler( + block_size=self.block_size, + page_size=page_size, + num_groups=self.num_groups, + group_size=group_size, + peak_activation_per_token=(config.hidden_size + config.vocab_size), + num_attention_masks=num_attention_masks, + ) + num_blocks, max_batch_tokens = memory_handler.infer_num_blocks_and_max_batch_tokens( + num_blocks=getattr(generation_config, "num_blocks", None), + max_batch_tokens=getattr(generation_config, "max_batch_tokens", None), + max_memory_percent=getattr(generation_config, "max_memory", 0.9), + cache_dtype=self.dtype, + ) + + # Add the inferred attributes to the class + self.num_blocks = num_blocks + self.max_batch_tokens = max_batch_tokens + logger.info( + f"PagedAttentionCache initialized with {self.num_blocks = }, {self.block_size = }, {page_size = }, " + f"{self.max_batch_tokens = } {num_attention_masks = }" + ) + + # Initialize the cache + self.key_cache: list[torch.Tensor] = [] + self.value_cache: list[torch.Tensor] = [] + # We add one extra token to the cache to handle padding and generally discard unwanted tokens + self.cache_shape = (num_blocks * self.block_size + 1, self.num_key_value_heads, self.head_dim) + for _ in range(group_size): + new_layer_key_cache = torch.empty(self.cache_shape, dtype=self.dtype, device=self.device) + new_layer_value_cache = torch.empty(self.cache_shape, dtype=self.dtype, device=self.device) + torch._dynamo.mark_static_address(new_layer_key_cache) + torch._dynamo.mark_static_address(new_layer_value_cache) + self.key_cache.append(new_layer_key_cache) + self.value_cache.append(new_layer_value_cache) + logger.info(f"{self.cache_shape = } {self.key_cache[0].shape = } {self.key_cache[0].numel() = }") + + # Block management data structures + self._free_blocks = deque(range(num_blocks)) + self.group_cache_managers: list[CacheAllocator] = [] + for i, group_type in enumerate(group_types): + if group_type == "full_attention": + cm = FullAttentionCacheAllocator(i, self.block_size) + elif group_type == "sliding_attention": + cm = SlidingAttentionCacheAllocator(i, self.block_size, config.sliding_window) + else: + raise ValueError(f"Invalid group type: {group_type}") + self.group_cache_managers.append(cm) + + @traced + def allocate_blocks(self, n_blocks: int, request_id: str) -> int: + """Allocate cache blocks across all layer groups for a given request. Actual allocation is done by the cache + managers, and this method only returns the maximum number of blocks actually allocated across all managers.""" + max_allocated = 0 + for cm in self.group_cache_managers: + allocated = cm.allocate_blocks(n_blocks, request_id, self._free_blocks) + if allocated is None: + return None + max_allocated = max(max_allocated, allocated) + return max_allocated + + @traced + def free_blocks(self, request_id: str) -> None: + """Free all allocated cache blocks for a given request across all layer groups. Actual deallocation is done + by the cache managers.""" + for cm in self.group_cache_managers: + cm.free_blocks(request_id, self._free_blocks) + + def get_num_free_blocks(self) -> int: + """Get the current number of unallocated blocks available for new requests.""" + return len(self._free_blocks) + + @traced + def extend_read_indices( + self, request_id: str, past_length: int, query_length: int, read_index: list[list[int]] + ) -> None: + """Retrieve physical cache indices for reading KV states in the cache across all layer groups. This method + coordinates with all cache managers to build the complete set of read indices needed for attention computation. + """ + for cm, read_indices in zip(self.group_cache_managers, read_index): + indices = cm.get_read_indices(request_id, past_length, query_length) + read_indices.extend(indices) + + @traced + def extend_write_indices( + self, request_id: str, past_length: int, query_length: int, write_index: list[list[int]] + ) -> None: + """Retrieve physical cache indices for writing new KV states to the cache across all layer groups. This method + coordinates with all cache managers to build the complete set of write indices needed to store computed KV + states.""" + for cm, write_indices in zip(self.group_cache_managers, write_index): + indices = cm.get_write_indices(request_id, past_length, query_length) + write_indices.extend(indices) + + @traced + def get_seqlens_k(self, request_id: str, past_length: int, query_length: int) -> dict[str, int]: + """Retrieve the key sequence length for the given request_id across all layer types. Returns a dictionary of + layer types to their corresponding key sequence lengths.""" + seqlens_k = {} + for cm in self.group_cache_managers: + attn_type, seqlen_k = cm.get_seqlens_k(request_id, past_length, query_length) + seqlens_k[attn_type] = seqlen_k + return seqlens_k + + @traced + def update( + self, + key_states: torch.Tensor, # shape [1, num_kv_heads, seqlen_kv, head_dim] + value_states: torch.Tensor, # shape [1, num_kv_heads, seqlen_kv, head_dim] + layer_idx: int, + read_index: list[torch.Tensor], # shape [num_layer_groups, seqlen_kv + past_length] + write_index: list[torch.Tensor], # shape [num_layer_groups, seqlen_q] + **kwargs, + ) -> tuple[torch.Tensor, torch.Tensor]: # shape [seqlen_kv + past_length, num_kv_heads, head_dim] + """Update the cache with new key-value states for a specific layer. This method writes new KV states to the + appropriate cache locations. The behavior differs based on the layer's attention type: + + - Full attention: New KV states are written to cache, then complete sequence is read from cache + - Sliding window: Old KV is read from cache along with extra spaces for the new KV, then new KV is written to + cache. This is because new KV might overwrite the old KV, so we need to read the old KV first. + + Returns the complete KV states (cached + new) for attention computation. + """ + # Retrieve the layer read and write indices, and if there is a sliding window + group_idx, layer_idx_in_group = self.layer_index_to_group_indices[layer_idx] + layer_read_index = read_index[group_idx] + layer_write_index = write_index[group_idx] + # Select the correct cache + k_cache = self.key_cache[layer_idx_in_group] + v_cache = self.value_cache[layer_idx_in_group] + # Transpose the key and value states to match the cache shape, after which shape is [seqlen_kv, num_kv_heads, head_dim] + key_states = key_states.transpose(1, 2).squeeze(0) + value_states = value_states.transpose(1, 2).squeeze(0) + + # Case: full attention + sliding_window = self.sliding_windows[layer_idx] + if sliding_window == 1: + k_cache[layer_write_index, :, :] = key_states + v_cache[layer_write_index, :, :] = value_states + key_states_with_cache = k_cache[layer_read_index, :, :] + value_states_with_cache = v_cache[layer_read_index, :, :] + + # Case: sliding window -- we need to be careful of read/write order because of chunked prefill, because it's + # the only case where you may write over cache you need to use + else: + # Add the cache to the key and value states + mask = layer_read_index == -1 # TODO: can this can be efficiently precomputed? + key_states_with_cache = k_cache[layer_read_index, :, :] + key_states_with_cache[mask] = key_states + value_states_with_cache = v_cache[layer_read_index, :, :] + value_states_with_cache[mask] = value_states + # Write new KV values to the cache + k_cache[layer_write_index, :, :] = key_states + v_cache[layer_write_index, :, :] = value_states + + # Return the new KV values + return key_states_with_cache, value_states_with_cache + + +# TODO: rework computation with the groups and their sizes +class PagedAttentionMemoryHandler: + """A helper class to determine the best number of pages and maximum number of tokens per batch for the paged + attention cache, providing automatic sizing based on available GPU memory. + The helper works using the number of pages, which is tied to the number of blocks by: + num_blocks = num_pages // block_size + + The memory footprint consists of three main components: + - Cache memory: the space needed to store the cache tensors: + 2 * layer_group_size * [num_pages, page_size] * cache_dtype + - Activation memory: the space temporarily taken by the largest activation during the model forward pass: + peak_activation_per_token * max_tokens_per_batch * activation_dtype_size + - Static tensors: the space taken by the input/output buffers and metadata tensors for batch processing, sum of: + - inputs_ids + outputs_ids + position_ids + logits_indices: 4 * max_tokens_per_batch * int32_size + - attention_mask: num_attention_masks * num_pages * max_tokens_per_batch * activation_dtype_size + - cumulative_seqlens_q + cumulative_seqlens_k: (1 + 2) * max_tokens_per_batch * int32_size + - write_index_tensor: num_groups * max_tokens_per_batch * int32_size + - read_index_tensor: num_groups * (num_pages + max_tokens_per_batch) * int32_size + + The handler can operate in three modes: + 1. Auto-sizing: Determines both number of pages and maximum number of tokens per batch using quadratic optimization + 2. Fixed cache: Calculates max batch tokens given a fixed number of pages + 3. Fixed batch: Calculates number of pages given a fixed maximum batch size + + """ + + _activation_dtype = torch.bfloat16 + _input_dtype = torch.int32 + _upper_bound_max_batch_tokens = 256 + _upper_bound_num_blocks = 4096 + + def __init__( + self, + block_size: int, + page_size: int, + num_groups: int, + group_size: int, + peak_activation_per_token: int, + num_attention_masks: int, + ) -> None: + """Initialize the memory handler with the parameters that cannot be automatically inferred. + + Args: + block_size: Size of the cache blocks + page_size: Size of the cache pages + num_groups: Number of layer groups + group_size: Number of layers per layer group + peak_activation_per_token: Maximum size of activation tensor per token, = hidden_size + vocab_size + num_attention_masks: Number of attention masks, 0 if no attention mask is used, 2 if hybrid model, else 1 + """ + self.block_size = block_size + self.page_size = page_size + self.num_groups = num_groups + self.group_size = group_size + self.peak_activation_per_token = peak_activation_per_token + self.num_attention_masks = num_attention_masks + + @staticmethod + def get_available_memory(max_memory_percent: float = 1.0) -> int: + """Calculate available GPU memory for cache allocation, accounting for already allocated tensors. + This method queries the current memory state and applies the specified percentage limit to determine + how much memory can be safely used for the paged attention cache. + + Args: + max_memory_percent: Fraction of available memory to use (0.0-1.0). 1.0 means use all available memory. + + Returns: + int: Available memory in bytes for cache allocation + """ + _, total, reserved, allocated = get_device_and_memory_breakdown() + available_memory = total - max(allocated, reserved) + available_memory = int(available_memory * max_memory_percent) + return available_memory + + def infer_num_blocks_and_max_batch_tokens( + self, + num_blocks: Optional[int] = None, + max_batch_tokens: Optional[int] = None, + max_memory_percent: float = 0.9, + cache_dtype: torch.dtype = torch.float16, + ) -> tuple[int, int]: + """Determine optimal number of blocks and maximum number of tokens per batch based on available memory and + constraints. Check the class docstring for more details. Naming the number of pages as N and the maximum number + of tokens per batch as M, the equation solved is: + + available_memory = sum([ + MN * num_attention_masks * activation_dtype_size, + 2N * (layer_group_size * page_size * cache_dtype + 2 * num_group), + M * (peak_activation_per_token * activation_dtype + 28 + 4 * num_group), + ]) + + where we already simplified int32_size = 4. + """ + # If neither num_blocks nor max_batch_tokens are provided, we use a second-order polynomial + if num_blocks is None and max_batch_tokens is None: + num_blocks, max_batch_tokens = self.compute_num_blocks_and_max_batch_tokens( + max_memory_percent, cache_dtype + ) + # If only num_blocks is provided, we infer the max_batch_tokens + elif num_blocks is not None and max_batch_tokens is None: + max_batch_tokens = self.compute_max_batch_tokens(num_blocks, max_memory_percent, cache_dtype) + # If only max_batch_tokens is provided, we infer the num_blocks + elif max_batch_tokens is not None and num_blocks is None: + num_blocks = self.compute_num_blocks(max_batch_tokens, max_memory_percent, cache_dtype) + + # We check if the memory footprint is too large in all cases + available_memory = self.get_available_memory(max_memory_percent) + memory_footprint = self.compute_memory_footprint( + max_batch_tokens=max_batch_tokens, + num_blocks=num_blocks, + cache_dtype=cache_dtype, + ) + if memory_footprint > available_memory: + raise MemoryError(f"Memory footprint {memory_footprint} is more than available memory {available_memory}") + return num_blocks, max_batch_tokens + + def compute_num_blocks_and_max_batch_tokens( + self, + max_memory_percent: float = 0.9, + cache_dtype: torch.dtype = torch.float16, + m: float = 0.01, + ) -> tuple[int, int]: + """Calculate optimal number of blocks and maximum number of tokens per batch using quadratic optimization when + neither is fixed. This method assumes a relationship M = m * N where m is a small ratio below 1 and solves the + resulting quadratic equation to find the optimal N that maximizes utilization within memory constraints. m is + the amount of cache we can fill with one batch: m=0.01 means a batch fills at most 1% of the cache. The equation + to solve is: + + available_memory = sum([ + m * N^2 * num_attention_masks * activation_dtype_size, + 2N * (layer_group_size * page_size * cache_dtype + 2 * num_group), + m * N * (peak_activation_per_token * activation_dtype + 28 + 4 * num_group), + ]) + """ + cache_memory = self.get_available_memory(max_memory_percent) + logger.info(f"Cache memory: {cache_memory}") + + # Compute second-degree polynomial coefficients + a = m * self.num_attention_masks * self._activation_dtype.itemsize + b = 2 * (self.group_size * self.page_size * cache_dtype.itemsize + 2 * self.num_groups) + b += m * (self.peak_activation_per_token * self._activation_dtype.itemsize + 28 + 4 * self.num_groups) + c = -cache_memory + logger.debug(f"Coefficients of 2nd degree polynomial: {a = }, {b = }, {c = }") + + # Compute discriminant and greatest solution + discriminant = b**2 - 4 * a * c + if discriminant < 0: + raise ValueError(f"Discriminant is negative: {discriminant = }") + greatest_solution = (-b + sqrt(discriminant)) / (2 * a) + if greatest_solution < 0: + raise ValueError(f"Greatest solution is negative: {greatest_solution = }") + + # Infer number of blocks and max batch tokens + num_pages = floor(greatest_solution) + num_blocks = num_pages // self.block_size + if num_blocks > self._upper_bound_num_blocks: + logger.info(f"{num_blocks = } is too large, setting to {self._upper_bound_num_blocks = }") + num_blocks = self._upper_bound_num_blocks + max_batch_tokens = int(greatest_solution * m) + if max_batch_tokens > self._upper_bound_max_batch_tokens: + logger.info(f"{max_batch_tokens = } is too large, setting to {self._upper_bound_max_batch_tokens = }") + max_batch_tokens = self._upper_bound_max_batch_tokens + return num_blocks, max_batch_tokens + + def compute_max_batch_tokens( + self, + num_blocks: int, + max_memory_percent: float = 0.9, + cache_dtype: torch.dtype = torch.float16, + ) -> int: + """Calculate maximum batch tokens M given a fixed number of cache blocks. The formula for M is given by: + + M = (available_memory - 2N * (layer_group_size * page_size * cache_dtype + 2 * num_group)) + / (activation_dtype_size * (N * num_attention_masks + peak_activation_per_token) + 28 + 4 * num_group) + """ + cache_memory = self.get_available_memory(max_memory_percent) + num_pages = num_blocks * self.block_size + # Compute numerator + num = cache_memory + num -= 2 * num_pages * (self.group_size * self.page_size * cache_dtype.itemsize + 2 * self.num_groups) + # Compute denominator + denum = self._activation_dtype.itemsize * ( + num_pages * self.num_attention_masks + self.peak_activation_per_token + ) + denum += 28 + 4 * self.num_groups + # Compute max batch tokens and return + max_batch_tokens = floor(num / denum) + if max_batch_tokens > self._upper_bound_max_batch_tokens: + logger.info(f"{max_batch_tokens = } is too large, setting to {self._upper_bound_max_batch_tokens = }") + max_batch_tokens = self._upper_bound_max_batch_tokens + return max_batch_tokens + + def compute_num_blocks( + self, + max_batch_tokens: int, + max_memory_percent: float = 0.9, + cache_dtype: torch.dtype = torch.float16, + ) -> int: + """Calculate number of cache blocks N given a fixed maximum token per token M. The formula for N is given by: + + N = (available_memory - M * (peak_activation_per_token * activation_dtype + 28 + 4 * num_group)) + / (2 * (layer_group_size * page_size * cache_dtype + 2 * num_group) + M * (num_attention_masks * activation_dtype_size)) + """ + cache_memory = self.get_available_memory(max_memory_percent) + # Compute numerator + num = cache_memory + num -= max_batch_tokens * self.peak_activation_per_token * self._activation_dtype.itemsize + num -= max_batch_tokens * (28 + 4 * self.num_groups) + # Compute denominator + denum = 2 * (self.group_size * self.page_size * cache_dtype.itemsize + 2 * self.num_groups) + denum += max_batch_tokens * (self.num_attention_masks * self._activation_dtype.itemsize) + denum += max_batch_tokens * self._activation_dtype.itemsize + # Compute cache size and return number of blocks + num_pages = floor(num / denum) + num_blocks = num_pages // self.block_size + if num_blocks > self._upper_bound_num_blocks: + logger.info(f"{num_blocks = } is too large, setting to {self._upper_bound_num_blocks = }") + num_blocks = self._upper_bound_num_blocks + return num_blocks + + def compute_memory_footprint( + self, + num_blocks: Optional[int] = None, + max_batch_tokens: Optional[int] = None, + cache_dtype: torch.dtype = torch.float16, + ) -> tuple[int, int, int]: + """Calculate the memory footprint breakdown for a given number of blocks and maximum batch tokens. The memory + footprint is given by: + + available_memory = sum([ + MN * num_attention_masks * activation_dtype_size, + 2N * (layer_group_size * page_size * cache_dtype + 2 * num_group), + M * (peak_activation_per_token * activation_dtype + 28 + 4 * num_group), + ]) + but is broken down below. + """ + num_pages = num_blocks * self.block_size + + cache_memory_footprint = 2 * self.group_size * num_pages * self.page_size * cache_dtype.itemsize + + activation_memory_footprint = self.peak_activation_per_token * self._activation_dtype.itemsize + activation_memory_footprint *= max_batch_tokens + + inputs_outputs_positions_and_logits_memory_footprint = 4 * max_batch_tokens * 4 # second 4 is for int32 size + + attention_memory_footprint = self.num_attention_masks * self._activation_dtype.itemsize + attention_memory_footprint *= num_pages * max_batch_tokens + + cumulative_seqlens_memory_footprint = 3 * max_batch_tokens * 4 # 4 is for int32 size + + write_index_memory_footprint = self.num_groups * max_batch_tokens * 4 # 4 is for int32 size + read_index_memory_footprint = self.num_groups * (num_pages + max_batch_tokens) * 4 # 4 is for int32 size + + total_memory_footprint = sum( + [ + cache_memory_footprint, + activation_memory_footprint, + inputs_outputs_positions_and_logits_memory_footprint, + attention_memory_footprint, + cumulative_seqlens_memory_footprint, + write_index_memory_footprint, + read_index_memory_footprint, + ] + ) + return total_memory_footprint diff --git a/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/cache_manager.py b/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/cache_manager.py new file mode 100644 index 0000000000000000000000000000000000000000..7e2d4f2b553278f3dcbf3b7d4a46b6a0f2f9f0dd --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/cache_manager.py @@ -0,0 +1,226 @@ +# coding=utf-8 +# Copyright 2025 The HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from abc import ABC, abstractmethod +from collections import deque +from math import ceil +from typing import Optional + +from .requests import logger + + +class CacheAllocator(ABC): + """Abstract base class for cache managers. Cache managers keep track of per-request cache allocations, determine + when a new physical block needs to be allocated and compute physical indices for reading or writing to the cache.""" + + _index: int + _block_table: dict[str, list[int]] # request_id -> list of block_ids allocated to the request + + @abstractmethod + def allocate_blocks(self, n_blocks: int, request_id: str, free_blocks: deque[int]) -> Optional[int]: + """Allocates n_blocks for a given request_id. Returns the num of blocks allocated if successful and None + otherwise.""" + pass + + def free_blocks(self, request_id: str, free_blocks: deque[int]) -> None: + """Frees all blocks associated with a request_id.""" + if request_id in self._block_table: + blocks_to_free = self._block_table.pop(request_id) + free_blocks.extend(blocks_to_free) + else: + logger.warning( + f"CacheAllocator {self._index} attempted to free blocks for non-existent request_id: {request_id}" + ) + + @abstractmethod + def get_read_indices(self, request_id: str, past_length: int, query_length: int) -> list[int]: + """Returns the physical indices of where to read request_id's cache in the cache tensor.""" + pass + + @abstractmethod + def get_write_indices(self, request_id: str, past_length: int, query_length: int) -> list[int]: + """Returns the physical indices of where to write request_id's cache in the cache tensor.""" + pass + + @abstractmethod + def get_seqlens_k(self, request_id: str, past_length: int, query_length: int) -> tuple[str, int]: + """Returns the attention type of the cache allocator and the key sequence length for the given request_id.""" + pass + + +class FullAttentionCacheAllocator(CacheAllocator): + """Cache manager for a group of full attention layers.""" + + def __init__(self, index: int, block_size: int) -> None: + """Initializes the cache manager for a group of full attention layers. + Args: + - index: the index of the associated layer group + - block_size: the size of the blocks in the cache + """ + self._index = index + self.block_size = block_size + self._block_table = {} + + def allocate_blocks(self, n_blocks: int, request_id: str, free_blocks: deque[int]) -> Optional[int]: + """Allocate blocks for a given request_id. Returns the number of blocks allocated if successful and None + otherwise. For group of full attention layers, we always allocate the number of requested blocks.""" + if len(free_blocks) < n_blocks: + return None + if request_id not in self._block_table: + self._block_table[request_id] = [] + self._block_table[request_id].extend(free_blocks.popleft() for _ in range(n_blocks)) + return n_blocks + + def get_read_indices(self, request_id: str, past_length: int, query_length: int) -> list[int]: + """Returns the physical indices of where to read request_id's cache. For a group of full attention layers, we + first write the new cache to the cache tensor and then read the entire cache from the beginning to the end.""" + # Retrieve the block table for the request and raise an error if it doesn't exist + block_table = self._block_table.get(request_id) + if block_table is None: + raise ValueError(f"No block table found for request {request_id}") + # Compute the physical indices + physical_indices = [] + for i in range(past_length + query_length): + block_idx = i // self.block_size + block_offset = i % self.block_size + physical_index = block_table[block_idx] * self.block_size + block_offset + physical_indices.append(physical_index) + return physical_indices + + def get_write_indices(self, request_id: str, past_length: int, query_length: int) -> list[int]: + """Returns the physical indices for writing to the cache. For a group of full attention layers, we write the new + cache as a continuation of the existing cache for the same request.""" + block_table = self._block_table.get(request_id) + if block_table is None: + raise ValueError(f"No block table found for request {request_id}") + # Compute the physical indices + physical_indices = [] + for i in range(past_length, past_length + query_length): + block_idx = i // self.block_size + block_offset = i % self.block_size + physical_index = block_table[block_idx] * self.block_size + block_offset + physical_indices.append(physical_index) + return physical_indices + + def get_seqlens_k(self, request_id: str, past_length: int, query_length: int) -> tuple[str, int]: + """Returns the attention type of the cache allocator and the key sequence length for the given request_id.""" + seqlens_k = past_length + query_length + return "full_attention", seqlens_k + + +class SlidingAttentionCacheAllocator(CacheAllocator): + """Cache manager for sliding window attention layers.""" + + def __init__(self, index: int, block_size: int, sliding_window: int) -> None: + """Initializes the cache manager for a group of sliding window attention layers. + Args: + - index: the index of the associated layer group + - block_size: the size of the blocks in the cache + - sliding_window: the size of the sliding window + """ + self._index = index + self.block_size = block_size + self.sliding_window = sliding_window + self._max_blocks_per_request = ceil(self.sliding_window / self.block_size) + self._block_table = {} + + def allocate_blocks(self, n_blocks: int, request_id: str, free_blocks: deque[int]) -> Optional[int]: + """Allocate blocks for a given request_id. Returns the number of blocks allocated if successful and None + otherwise. For group of sliding window attention layers, we only allocate up to the point where we can fit an + entire sliding window in the cache tensor.""" + if request_id not in self._block_table: + self._block_table[request_id] = [] + # Early return if we are already at the max number of blocks per request + already_allocated = len(self._block_table[request_id]) + if already_allocated == self._max_blocks_per_request: + return 0 + # Compute actual number of blocks to allocate + after_allocation = min(already_allocated + n_blocks, self._max_blocks_per_request) + actual_n_blocks = after_allocation - already_allocated + # Classic allocation + if len(free_blocks) < actual_n_blocks: + return None + self._block_table[request_id].extend(free_blocks.popleft() for _ in range(actual_n_blocks)) + return actual_n_blocks + + def get_read_indices(self, request_id: str, past_length: int, query_length: int) -> list[int]: + """Returns the physical indices of where to read request_id's cache in the cache tensor. + For a group of sliding window attention layers, we read from the cache tensor before writing on it, because the + new cache can overwrite the old one. To form the cache + new key / values states, we read the at most + sliding_window - 1 cache page and then manually add the new key / values states after. Hence the -1 indices + which indicate where to store the new key or values indices.""" + # Retrieve the block table for the request and raise an error if it doesn't exist + block_table = self._block_table.get(request_id) + if block_table is None: + raise ValueError(f"No block table found for request {request_id}") + # Apply sliding window + start_index = 0 if past_length < self.sliding_window else past_length % self.sliding_window + cache_length = min(past_length, self.sliding_window - 1) + # Compute the physical indices + physical_indices = [] + for i in range(start_index, start_index + cache_length): + i %= self.sliding_window + block_idx = i // self.block_size + block_offset = i % self.block_size + physical_index = block_table[block_idx] * self.block_size + block_offset + physical_indices.append(physical_index) + return physical_indices + [-1] * query_length + + def get_write_indices(self, request_id: str, past_length: int, query_length: int) -> list[int]: + """Returns the physical indices of where to write request_id's cache in the cache tensor. For a group of + sliding window attention layers, we write the new cache in rolling-buffer kind of way: if we reach the end of + the allocated physical cache, we start writing from the beginning of the physical cache again.""" + # Retrieve the block table for the request and raise an error if it doesn't exist + block_table = self._block_table.get(request_id) + if block_table is None: + raise ValueError(f"No block table found for request {request_id}") + # Apply sliding window + start_index = past_length % self.sliding_window + cache_length = min(query_length, self.sliding_window) + padding_length = query_length - cache_length + # Compute the physical indices + physical_indices = [] + for i in range(start_index, start_index + cache_length): + i %= self.sliding_window + block_idx = i // self.block_size + block_offset = i % self.block_size + physical_index = block_table[block_idx] * self.block_size + block_offset + physical_indices.append(physical_index) + if padding_length > 0: + physical_indices = [-1] * padding_length + physical_indices + return physical_indices + + def get_seqlens_k(self, request_id: str, past_length: int, query_length: int) -> tuple[str, int]: + """Returns the attention type of the cache allocator and the key sequence length for the given request_id.""" + seqlens_k = query_length + min(past_length, self.sliding_window - 1) + return "sliding_attention", seqlens_k + + +# TODO: test the impact of this +# def get_read_indices(self, request_id: str, past_length: int) -> list[int]: +# # Retrieve the block table for the request and raise an error if it doesn't exist +# block_table = self._block_table.get(request_id) +# if block_table is None: +# raise ValueError(f"No block table found for request {request_id}") +# # Compute the physical indices +# physical_indices = [] +# n_left = past_length +# for block_idx in block_table: +# block_physical_index = block_idx * self.block_size +# pages_used = min(self.block_size, n_left) +# physical_indices.extend(block_physical_index + i for i in range(pages_used)) +# n_left -= pages_used +# if n_left == 0: +# return physical_indices +# raise ValueError(f"Request {request_id} required too many indices: {past_length = } and {len(block_table) = }") diff --git a/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/continuous_api.py b/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/continuous_api.py new file mode 100644 index 0000000000000000000000000000000000000000..0d1801fa163e137f69e128cebbaa36877eaaa28a --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/continuous_api.py @@ -0,0 +1,1047 @@ +# coding=utf-8 +# Copyright 2024 The HuggingFace Inc. team. +# Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import queue +import threading +from dataclasses import dataclass +from functools import partial +from itertools import count +from time import perf_counter +from typing import Optional, Union + +import torch +from torch import nn +from tqdm import tqdm + +from ...configuration_utils import PretrainedConfig +from ...generation.configuration_utils import GenerationConfig +from ...utils.logging import logging +from ...utils.metrics import ContinuousBatchProcessorMetrics, attach_tracer, traced +from .cache import PagedAttentionCache +from .requests import GenerationOutput, RequestState, RequestStatus, get_device_and_memory_breakdown, logger +from .scheduler import SCHEDULER_MAPPING, FIFOScheduler, Scheduler + + +def build_attention_mask( + attention_mask: torch.Tensor, + cumulative_seqlens_q: torch.Tensor, + cumulative_seqlens_k: torch.Tensor, + sliding_window: int = 1, +) -> None: + """Builds an attention mask inplace using the cumulative seqlens of the query and key. If given a sliding window, it + will also apply a sliding window mask on top. The attention mask is not boolean, it uses zeroes and -inf (or its + equivalent) so it's more of an attention score bias tensor. + The attention mask is a block-diagonal matrix, with each block an attention mask for a single query-key pair. + Each of those block is built from a causal mask and, if there is a sliding window, a sliding window mask. + + An example is represented below, with seqlen_k = 8, seqlen_q = 4 and sliding_window = 6: + + CAUSAL MASK: + + █ █ █ █ █ ░ ░ ░ + █ █ █ █ █ █ ░ ░ + █ █ █ █ █ █ █ ░ + █ █ █ █ █ █ █ █ + + SLIDING WINDOW MASK: + ┌──────────────────────── seqlen_k - seqlen_q - sliding_window = 8 - 4 - 6 = -2 offset to the right + <─┴─> + ░ █ | █ █ █ █ █ █ █ █ + ░ ░ | █ █ █ █ █ █ █ █ + ░ ░ | ░ █ █ █ █ █ █ █ + ░ ░ | ░ ░ █ █ █ █ █ █ + + ATTENTION MASK (sum of causal and sliding window masks): + + █ █ █ █ █ ░ ░ ░ + █ █ █ █ █ █ ░ ░ + ░ █ █ █ █ █ █ ░ + ░ ░ █ █ █ █ █ █ + + Another example with seqlen_k = 5, seqlen_q = 3 and sliding_window = 2: + + CAUSAL MASK: + + █ █ █ ░ ░ + █ █ █ █ ░ + █ █ █ █ █ + + SLIDING WINDOW MASK: + ┌──────────────────────── seqlen_k - seqlen_q - sliding_window = 5 - 3 - 2 = 0 offset to the right + <┴> + | ░ █ █ █ █ + | ░ ░ █ █ █ + | ░ ░ ░ █ █ + + ATTENTION MASK (sum of causal and sliding window masks): + + ░ █ █ ░ ░ + ░ ░ █ █ ░ + ░ ░ ░ █ █ + + """ + min_value = torch.finfo(attention_mask.dtype).min + for i in range(len(cumulative_seqlens_q) - 1): + seqlen_q = cumulative_seqlens_q[i + 1] - cumulative_seqlens_q[i] + seqlen_k = cumulative_seqlens_k[i + 1] - cumulative_seqlens_k[i] + if seqlen_q < seqlen_k and seqlen_q >= 1: + causal_diagonal = seqlen_k - seqlen_q + 1 + else: + causal_diagonal = 1 + query_range = slice(cumulative_seqlens_q[i], cumulative_seqlens_q[i + 1]) + key_range = slice(cumulative_seqlens_k[i], cumulative_seqlens_k[i + 1]) + # Apply causal mask + minus_inf = torch.full( + attention_mask[..., query_range, key_range].shape, + min_value, + dtype=attention_mask.dtype, + device=attention_mask.device, + ) + masked = torch.triu(minus_inf, diagonal=causal_diagonal) + # Apply sliding window mask if needed + if sliding_window > 1: + sliding_diagonal = seqlen_k - seqlen_q - sliding_window + masked += torch.tril(minus_inf, diagonal=sliding_diagonal) + # Replace in attention mask + attention_mask[..., query_range, key_range] = masked + + +@dataclass +class PagedAttentionArgs: + input_ids: torch.Tensor + attention_mask: Optional[torch.Tensor] + position_ids: torch.Tensor + cumulative_seqlens_q: torch.Tensor + cumulative_seqlens_k: torch.Tensor + max_seqlen_q: int + max_seqlen_k: int + write_index: list[torch.Tensor] + read_index: list[torch.Tensor] + logits_indices: torch.Tensor + cache: PagedAttentionCache + use_cache: bool = False + + +# Continuous Batch Processor (Internal Logic) +@attach_tracer() +class ContinuousBatchProcessor: + def __init__( + self, + cache: PagedAttentionCache, + config: PretrainedConfig, + generation_config: GenerationConfig, + input_queue: queue.Queue, + output_queue: queue.Queue, + stop_event: threading.Event, + model_device: torch.device, + model_dtype: torch.dtype, + scheduler: Scheduler, + streaming: bool = False, + manual_eviction: bool = False, + slice_inputs: bool = True, # TODO: There should be an heuristic to decide on slicing, compile, cuda graphs... + ) -> None: + """Initialize the continuous batch processor. + + Args: + cache: A [`PagedAttentionCache`] object + config: The model configuration + generation_config: The generation configuration + input_queue: Queue for incoming requests + output_queue: Queue for outgoing results + stop_event: Event to signal processing should stop + model_device: Device for model inputs/outputs + model_dtype: Data type for model inputs/outputs + scheduler: The [`Scheduler`] to use + streaming: Whether to stream tokens as they're generated + manual_eviction: Whether to manually evict blocks from the cache + slice_inputs: Whether to slice the inputs to the model + """ + self.cache = cache + self.config = config + self.generation_config = generation_config + self.input_queue = input_queue + self.output_queue = output_queue + self.stop_event = stop_event + self.model_device = model_device + self.model_dtype = model_dtype + self.scheduler = scheduler + self.streaming = streaming + self.manual_eviction = manual_eviction + self.slice_inputs = slice_inputs + + # Retrieve the size of the sliding window if there is one + self.sliding_window = 1 if getattr(config, "sliding_window", None) is None else config.sliding_window + + self.requests_in_batch: list[RequestState] = [] + + # Set up metrics collector + self.max_batch_tokens = cache.max_batch_tokens + self.metrics = ContinuousBatchProcessorMetrics(cache.max_batch_tokens) + + # Setup static tensors + self.total_query_length = 0 + self.total_key_length = 0 + self.total_batch_size = 0 + self.setup_static_tensors(cache.num_groups) + + @traced(standalone=True) + def setup_static_tensors(self, num_groups: int) -> None: + T = self.max_batch_tokens + num_pages = self.cache.num_blocks * self.cache.block_size + self.tensor_metadata = {"dtype": torch.int32, "device": self.model_device} + + # Some tensors always have the same shape regardless of the model + self.input_ids = torch.empty((1, T), **self.tensor_metadata) + self.position_ids = torch.empty((1, T), **self.tensor_metadata) + self.cumulative_seqlens_q = torch.empty((T + 1,), **self.tensor_metadata) + self.max_seqlen_q = 0 + self.logits_indices = torch.empty((T,), **self.tensor_metadata) + self.output_ids = torch.empty((1, T), **self.tensor_metadata) + + # For some kwargs, we have a dict of tensors with as many items as there are attention types + layer_types = getattr(self.config, "layer_types", None) + if layer_types is None: + sliding_window = getattr(self.config, "sliding_window", 1) + layer_types = ["full_attention"] if sliding_window in [1, None] else ["sliding_attention"] + layer_types = list(set(layer_types)) + + self.cumulative_seqlens_k = { + layer_type: torch.empty((T + 1), **self.tensor_metadata) for layer_type in layer_types + } + self.max_seqlen_k = dict.fromkeys(layer_types, 0) + + if self.return_attention_mask(): + attn_mask_kwargs = { + "size": (1, 1, T, num_pages + T), + "dtype": self.model_dtype, + "device": self.model_device, + } + self.attention_mask = {layer_type: torch.empty(**attn_mask_kwargs) for layer_type in layer_types} + else: + self.attention_mask = None + + # For other kwargs, we need a list of tensors with as many tensors as there are groups + self.write_index_storage = [torch.empty((T,), **self.tensor_metadata) for _ in range(num_groups)] + self.read_index_storage = [torch.empty((num_pages + T), **self.tensor_metadata) for _ in range(num_groups)] + # For read index, the +T is because there are -1 for seqlen_q when model uses a sliding window + + # After allocating empty tensors, we reset them to the right value + self.reset_static_tensors(full_reset=True) + + def return_attention_mask(self) -> bool: + return self.config._attn_implementation != "paged_attention" # we set `is_causal` to True in paged call + + @traced + @torch.no_grad() + def reset_static_tensors(self, full_reset: bool = False): + """Reset static tensors for the next batch. In between batches, reset only the parts that were used in the last + batch, but for initialisation, we can reset everything using the (full_reset) flag.""" + # Compute the slice to reset + if full_reset or not self.slice_inputs: + q_len = self.write_index_storage[0].size(-1) + k_len = self.read_index_storage[0].size(-1) + b_size = self.write_index_storage[0].size(0) + else: + q_len = self.total_query_length + k_len = self.total_key_length + b_size = self.total_batch_size + + # Reset the attributes that always have the same shape + self.input_ids[:, :q_len].zero_() + self.position_ids[:, :q_len].zero_() + self.cumulative_seqlens_q[: b_size + 1].zero_() + self.max_seqlen_q = 0 + self.logits_indices[:q_len].fill_(-1) + self.output_ids[:, :q_len].fill_(-1) + + # Reset the attributes that are either tensors or dict of tensors + for layer_type in self.cumulative_seqlens_k: + self.cumulative_seqlens_k[layer_type][: b_size + 1].zero_() + self.max_seqlen_k[layer_type] = 0 + if self.attention_mask is not None: + self.attention_mask[layer_type][:, :, :q_len, :k_len].fill_(torch.finfo(self.model_dtype).min) + + # Reset the attributes that are lists of tensors + for i in range(self.cache.num_groups): + self.write_index_storage[i][:q_len].fill_(-1) + self.read_index_storage[i][: q_len + k_len].fill_(-1) + + def get_model_kwargs(self) -> PagedAttentionArgs: + """Get model keyword arguments for the current batch.""" + # Compute the slice to return + q_len = self.total_query_length if self.slice_inputs else self.write_index_storage[0].size(-1) + b_size = self.total_batch_size if self.slice_inputs else self.cumulative_seqlens_q.size(-1) - 1 + + # Prepare the kwargs, the attributes that are either tensors or dict of tensors are initialized to empty dicts + kwargs = { + "input_ids": self.input_ids[:, :q_len], + "position_ids": self.position_ids[:, :q_len], + "cu_seq_lens_q": self.cumulative_seqlens_q[: b_size + 1], + "max_seqlen_q": self.max_seqlen_q, + "logits_indices": self.logits_indices[:q_len], + "cu_seq_lens_k": {}, + "max_seqlen_k": {}, + "attention_mask": {}, + "read_index": self.read_index, # slicing is done during building + "write_index": self.write_index, # slicing is done during building + "cache": self.cache, + "use_cache": False, + } + + # For the attributes that are dict of tensors, we replace the dict with a tensor if there is only one entry + layer_types = list(self.cumulative_seqlens_k.keys()) + if len(layer_types) > 1: + for layer_type, seqlens_k in self.cumulative_seqlens_k.items(): + kwargs["cu_seq_lens_k"][layer_type] = seqlens_k[: b_size + 1] + kwargs["max_seqlen_k"][layer_type] = self.max_seqlen_k[layer_type] + if self.attention_mask is not None: + k_len = seqlens_k[b_size] if self.slice_inputs else self.attention_mask[layer_type].size(-1) + kwargs["attention_mask"][layer_type] = self.attention_mask[layer_type][..., :q_len, :k_len] + else: + layer_type = layer_types[0] + kwargs["cu_seq_lens_k"] = self.cumulative_seqlens_k[layer_type][: b_size + 1] + kwargs["max_seqlen_k"] = self.max_seqlen_k[layer_type] + if self.attention_mask is not None: + k_len = self.cumulative_seqlens_k[layer_type][b_size] + k_len = k_len if self.slice_inputs else self.attention_mask[layer_type].size(-1) + kwargs["attention_mask"] = self.attention_mask[layer_type][..., :q_len, :k_len] + + if self.attention_mask is None: + kwargs["attention_mask"] = None + return kwargs + + def __repr__(self): + return ( + f"ContinuousBatchProcessor(input_queue={self.input_queue}, output_queue={self.output_queue}, " + f"active_requests={self.scheduler.active_requests}, waiting_requests={self.scheduler.waiting_requests})" + + self.get_model_kwargs().__repr__() + ) + + @traced + def _get_new_requests(self): + """Pull new requests from the input queue and add to waiting list.""" + while not self.input_queue.empty(): + try: + state = self.input_queue.get_nowait() + if state is None: # Sentinel value + continue + self.scheduler.add_waiting_request(state) + + except queue.Empty: + break + except Exception as e: + logger.error(f"Error processing new request: {e}", exc_info=True) + state: RequestState = locals().get("state") + if state is not None: + self._handle_request_error(e, state) + + @traced + def _handle_request_error(self, error, state: RequestState): + """Handle general request processing error.""" + state.status = RequestStatus.FAILED + state.error = str(error) + + # Include any generated tokens if this is an active request + if isinstance(state.request_id, str): + state.static_outputs = self.scheduler.get_active_request_static_outputs(state.request_id) + else: + state.static_outputs = [] + + self.metrics.record_request_completion(state.created_time, state.request_id) + self.output_queue.put(state.to_generation_output()) + + @traced + def prepare_next_batch(self) -> bool: + """Prepare tensors and metadata for the next model forward pass. Returns True if there are requests to process, + False otherwise.""" + + # Get new requests from the queue, stop if there are no pending requests + self._get_new_requests() + self.scheduler.clear_cancelled_requests() + if not self.scheduler.has_pending_requests(): + return False + self.metrics.record_queue_metrics(len(self.scheduler.active_requests), len(self.scheduler.waiting_requests)) + + # Schedule the next batch of requests, stop if there are no requests in the batch + self.requests_in_batch = self.scheduler.schedule_batch(self.max_batch_tokens) + if not self.requests_in_batch: + return False + self.metrics.record_batch_metrics(self.requests_in_batch) + + # Reset the static tensors used for storage + self.reset_static_tensors() # TODO: with slice_inputs, this might be unnecessary + + # Prepare accumulators + self.total_query_length = 0 + self.total_key_length = 0 + self.total_batch_size = 0 + + input_ids = [] + position_ids = [] + cumulative_seqlens_q = [0] + logits_indices = [] + + if isinstance(self.cumulative_seqlens_k, dict): + cumulative_seqlens_k = {layer_type: [0] for layer_type in self.cumulative_seqlens_k} + else: + cumulative_seqlens_k = [0] + + read_index = [[] for _ in range(self.cache.num_groups)] + write_index = [[] for _ in range(self.cache.num_groups)] + + # Go through all the requests in the batch + for state in self.requests_in_batch: + # First we retrieve the lengths related to the request + past_length = state.position_offset + query_length = len(state.prompt_ids) + seqlens_k = self.cache.get_seqlens_k(state.request_id, past_length, query_length) + + # Then we update the total lengths that are used for slicing + self.total_query_length += query_length + # total_key_length is used to slice the keys so we need to take the max of all the key lengths + self.total_key_length += max(seqlens_k.values()) + self.total_batch_size += 1 + # And the attribute tracking the position in the request object + state.position_offset += query_length + + # Then we accumulate for the object used in the kwargs + input_ids.extend(state.prompt_ids) + position_ids.extend(range(past_length, past_length + query_length)) + cumulative_seqlens_q.append(cumulative_seqlens_q[-1] + query_length) + self.max_seqlen_q = max(self.max_seqlen_q, query_length) + + if not state.remaining_prompt_ids: + logits_indices.append(cumulative_seqlens_q[-1] - 1) + + for layer_type, layer_type_seqlen_k in seqlens_k.items(): + cumulative_seqlens_k[layer_type].append(cumulative_seqlens_k[layer_type][-1] + layer_type_seqlen_k) + self.max_seqlen_k[layer_type] = max(self.max_seqlen_k[layer_type], layer_type_seqlen_k) + + self.cache.extend_read_indices(state.request_id, past_length, query_length, read_index) + self.cache.extend_write_indices(state.request_id, past_length, query_length, write_index) + + # When looping over request is done, we can build the actual tensors + self._build_tensors( + input_ids, + position_ids, + read_index, + write_index, + cumulative_seqlens_q, + cumulative_seqlens_k, + logits_indices, + ) + self.metrics.record_kv_cache_memory_metrics(self.cache) + + if logger.isEnabledFor(logging.DEBUG): + if isinstance(self.cumulative_seqlens_k, dict): + ck = max(cumulative_seqlens_k[layer_type][-1] for layer_type in self.cumulative_seqlens_k) + else: + ck = cumulative_seqlens_k[-1] + logger.debug( + f"Scheduled: {len(self.requests_in_batch)}, Waiting: {len(self.scheduler.waiting_requests)}, " + f"Active: {len(self.scheduler.active_requests)}. cum Q: {cumulative_seqlens_q[-1]}. " + f"cum KV: {ck}, free blocks: {self.cache.get_num_free_blocks()}" + ) + return True + + @traced + def _build_tensors( + self, + input_ids: list[int], + position_ids: list[int], + read_index: list[list[int]], + write_index: list[list[int]], + cumulative_seqlens_q: list[int], + cumulative_seqlens_k: Union[list[int], dict[str, list[int]]], + logits_indices: list[int], + ) -> None: + """Builds the actual tensors for the current batch, by modifying the already allocated tensors in place.""" + to_tensor = partial(torch.tensor, **self.tensor_metadata) + + # Those kwargs always have the same type regardless of the model + self.input_ids[:, : len(input_ids)] = to_tensor(input_ids) + self.position_ids[:, : len(position_ids)] = to_tensor(position_ids) + self.cumulative_seqlens_q[: len(cumulative_seqlens_q)] = to_tensor(cumulative_seqlens_q) + self.logits_indices[: len(logits_indices)] = to_tensor(logits_indices) + + # Those kwargs are either dict of tensors or tensors, so we need to handle both cases + for layer_type, layer_type_seqlens_k in cumulative_seqlens_k.items(): + self.cumulative_seqlens_k[layer_type][: len(layer_type_seqlens_k)] = to_tensor(layer_type_seqlens_k) + if self.attention_mask is not None: + build_attention_mask( + attention_mask=self.attention_mask[layer_type], + cumulative_seqlens_q=cumulative_seqlens_q, + cumulative_seqlens_k=layer_type_seqlens_k, + sliding_window=self.sliding_window if layer_type == "sliding_attention" else 1, + ) + + # The index only contain references to the storage tensors, so we update the storage and their references + self.read_index = [] + self.write_index = [] + for i, group_read_indices, group_write_indices in zip(count(), read_index, write_index): + # Write in the actual tensors + self.read_index_storage[i][: len(group_read_indices)] = to_tensor(group_read_indices) + self.write_index_storage[i][: len(group_write_indices)] = to_tensor(group_write_indices) + # Slice to the right size + r = len(group_read_indices) if self.slice_inputs else self.read_index_storage[i].size(-1) + w = len(group_write_indices) if self.slice_inputs else self.write_index_storage[i].size(-1) + # Add to the index + self.read_index.append(self.read_index_storage[i][:r]) + self.write_index.append(self.write_index_storage[i][:w]) + + @traced + def _sync(self): + if self.output_ids is not None: + try: + out = self.output_ids.tolist()[0] # should be the only sync we do + except Exception: + out = [0, 1] + else: + out = [0, 0] + return out + + @traced + def _maybe_send_output(self, state: RequestState, token: int): + """Send output to the queue based on streaming mode and request state.""" + if self.streaming: + self.output_queue.put(state.to_generation_output()) + elif state.status == RequestStatus.FINISHED: + self.output_queue.put(state.to_generation_output()) + + @traced + def update_batch(self): + """Update request states based on generated tokens.""" + out_tokens = self._sync() + finished_request_ids = [] + for i, state in enumerate(self.requests_in_batch): + req_id = state.request_id + if len(state.remaining_prompt_ids) == 0: + self.metrics.record_ttft_metric(state.created_time, state.request_id) + state.status = RequestStatus.DECODING + token = out_tokens[self.logits_indices[i]] + state.prompt_ids = [token] + if state.update_with_token(token): + self.metrics.record_request_completion(state.created_time, state.request_id) + self.scheduler.finish_request(state.request_id, evict_from_cache=(not self.manual_eviction)) + finished_request_ids.append(req_id) + self._maybe_send_output(state, token) + elif state.status == RequestStatus.PREFILLING_SPLIT: + state.status = RequestStatus.SPLIT_PENDING_REMAINDER + if self.cache.get_num_free_blocks() == 0: + raise ValueError("No more free blocks") + + @traced + def has_pending_requests(self) -> bool: + """Check if there are any active or waiting requests.""" + return self.scheduler.has_pending_requests() + + @traced + def handle_batch_error(self, error): + """Handle errors during batch processing.""" + failed_reqs = self.requests_in_batch + for req in failed_reqs: + self._handle_request_error(error, req) + self.scheduler.finish_request(req.request_id) + + @traced + def fail_all_requests(self, error): + """Fail all active requests with the given error. + + Args: + error: The error to report in the failure message + """ + + requests = list(self.scheduler.active_requests.values()) + for state in requests: + self._handle_request_error(error, state) + self.scheduler.finish_request(state.request_id) + + # Also fail any requests in the waiting queue + for req_id in list(self.scheduler.waiting_requests.keys()): + state = self.scheduler.waiting_requests.pop(req_id) + self._handle_request_error(error, state) + + # Clear the ordering queue + self.scheduler.waiting_requests_order.clear() + + +# Manager Class (User Interface) +@attach_tracer() +class ContinuousBatchingManager: + """Manager for handling continuous batching of generation requests. + + This class provides the user interface for submitting generation requests, + retrieving results, and managing the background generation thread. + """ + + def __init__( + self, + model, + generation_config: GenerationConfig, + manual_eviction: bool = False, + max_queue_size=0, + streaming: bool = True, + slice_inputs: bool = True, + ): + """Initialize the continuous batching manager. + + Args: + model: The language model for generation + generation_config: Configuration for generation parameters + max_queue_size: Maximum size of the request queue (0 = unlimited) + streaming: Whether to stream tokens as they are generated + """ + self.model = model.eval() + generation_config = model.generation_config if generation_config is None else generation_config + self.generation_config = generation_config + self.input_queue = queue.Queue(maxsize=max_queue_size) + self.output_queue = queue.Queue() + self.stop_event = threading.Event() + self.streaming = streaming + self.log_prob_generation = getattr(generation_config, "log_prob_generation", False) + self._generation_thread = None + self._request_counter = 0 + self._request_lock = threading.Lock() + self.model.generation_config.top_p = None + self.do_sample = getattr(generation_config, "do_sample", True) + self.logit_processor = self.model._get_logits_processor(generation_config) + self.use_cuda_graph = getattr(generation_config, "use_cuda_graph", False) # TODO: same as do_sample + self.profile = getattr(generation_config, "profile", False) + self.manual_eviction = manual_eviction + self.batch_processor: Optional[ContinuousBatchProcessor] = None + self.slice_inputs = slice_inputs + + if self.use_cuda_graph: + raise NotImplementedError("Cuda graphs are not supported yet") + + @traced + def start(self): + """Start the background generation thread.""" + if self._generation_thread is not None and self._generation_thread.is_alive(): + logger.warning("Manager thread is already running.") + return + + self._result_queue = queue.Queue() + self._generation_thread = threading.Thread(target=self._run_generation_loop) + self._generation_thread.start() + + def is_running(self): + """Check if the background generation thread is running.""" + return self._generation_thread is not None and self._generation_thread.is_alive() + + def stop(self, block: bool = False, timeout: Optional[float] = None): + """Signal the background thread to stop. + + Args: + block: Whether to wait for the thread to stop + timeout: Maximum time to wait for the thread to stop + """ + if self._generation_thread is None: + logger.warning("Manager not started.") + return + + if not self.stop_event.is_set(): + self.stop_event.set() + logger.info("Stopping continuous batching manager...") + + if block: + self.join(timeout) + + def join(self, timeout: Optional[float] = None): + """Wait for the background thread to finish. + + Args: + timeout: Maximum time to wait for the thread to stop + """ + if self._generation_thread is not None: + self._generation_thread.join(timeout=timeout) + if self._generation_thread.is_alive(): + logger.warning("Generation thread did not exit after join timeout.") + else: + logger.info("Continuous Batching Manager stopped.") + self._generation_thread = None + + def add_request( + self, input_ids: list[int], request_id: Optional[str] = None, max_new_tokens: Optional[int] = None + ) -> str: + """Add a new generation request to the queue. + + Args: + input_ids: Input token IDs to use as prompt + request_id: Optional custom request ID (auto-generated if None) + **kwargs: Additional generation parameters + + Returns: + str: The request ID + """ + if request_id is None: + with self._request_lock: + request_id = f"req_{self._request_counter}" + self._request_counter += 1 + + max_new_tokens = self.generation_config.max_new_tokens if max_new_tokens is None else max_new_tokens + + # NOTE: do we want to handle a case when the user wants token ids returned instead of decoded text? + state = RequestState( + request_id=request_id, + prompt_ids=list(input_ids), + full_prompt_ids=list(input_ids), + max_new_tokens=max_new_tokens, + eos_token_id=self.generation_config.eos_token_id, + ) + + # Use block=True with timeout to handle backpressure if queue is full + self.input_queue.put(state, block=True, timeout=10) # XXX: pass timeout as fn arg? + logger.debug(f"Added request {request_id} to queue.") + return request_id + + def add_requests(self, inputs: list[list[int]], **kwargs): + for input_ids in inputs: + self.add_request(input_ids, **kwargs) + + def cancel_request(self, request_id: str): + """Cancel a request by its ID. + + Args: + request_id: The ID of the request to cancel + """ + if self.batch_processor is not None: + self.batch_processor.scheduler.set_request_cancellation(request_id) + + def get_result(self, request_id=None, timeout=None) -> Optional[GenerationOutput]: + """Retrieve one result from the output queue. + + Args: + timeout: Maximum time to wait for a result + + Returns: + Optional[GenerationOutput]: The result data or None if timeout + """ + if self._generation_thread is None and self.output_queue.empty(): + return None + try: + result = self.output_queue.get(block=True, timeout=timeout) + if request_id is not None and result.request_id != request_id: + self.output_queue.put(result) + return None + logger.debug(f"Retrieved result for request {result.request_id}") + return result + except queue.Empty: + return None + + def __iter__(self): + """Iterate over results as they become available.""" + while self._generation_thread is not None and self._generation_thread.is_alive(): + result = self.get_result(timeout=0.1) + if result is not None: + yield result + + def request_id_iter(self, request_id): + """Iterate over results matching a specific request id as they become available.""" + request_cancelled = False + while self._generation_thread is not None and self._generation_thread.is_alive() and not request_cancelled: + result = self.get_result(request_id=request_id, timeout=0.1) + if result is not None: + yield result + if self.batch_processor is not None: + request_cancelled = self.batch_processor.scheduler.request_is_cancelled(request_id) + + @staticmethod + def supported_attention_implementations() -> set[str]: + return {"eager_paged", "sdpa_paged", "flash_attention_2"} + + @staticmethod + def default_attention_implementation() -> str: + return "sdpa_paged" + + @traced + def warmup(self, batch_processor): + stream = torch.cuda.Stream(device=self.model.device) + stream.wait_stream(torch.cuda.current_stream()) + with torch.cuda.stream(stream): + # Warmup the model with a dummy forward pass + self._generation_step(batch_processor) + torch.cuda.current_stream().wait_stream(stream) + + self.graph = torch.cuda.CUDAGraph() + with torch.cuda.graph(self.graph, stream=stream): + self._generation_step(batch_processor) + + @traced + # @torch.compile + def _generation_step(self, batch_processor: ContinuousBatchProcessor): + """Perform a single generation step. This is cuda graphed""" + batch_data = batch_processor.get_model_kwargs() + with torch.no_grad(): + logits = self._model_forward(batch_data) + if self.log_prob_generation: + batch_processor.output_probs.copy_(logits) # TODO + probs = self._process_logit(batch_data, logits) + self._sample(batch_processor, probs) + + @traced(span_name="model_forward") + def _model_forward(self, batch_data): + return self.model(**batch_data).logits + + @traced(span_name="logit_processing") + def _process_logit(self, batch_data, logits): + # Pass continuous batching context to logits processor if it supports it. TODO we should find a way to make this a little bit cleaner! + if hasattr(self.logit_processor, "set_continuous_batching_context"): + self.logit_processor.set_continuous_batching_context( + batch_data["logits_indices"], batch_data["cu_seq_lens_q"] + ) + + # Handle shape compatibility: logit processors expect 2D tensors [batch_size, vocab_size] + # but continuous batching always produces 3D tensors [batch_size, seq_len, vocab_size] + batch_size, seq_len, vocab_size = logits.shape + logits_2d = logits.view(batch_size * seq_len, vocab_size) + input_ids_2d = batch_data["input_ids"].view(batch_size * seq_len) + + # Process with 2D tensors + processed_logits_2d = self.logit_processor(input_ids_2d, logits_2d) + + # Reshape back to 3D + return processed_logits_2d.view(batch_size, seq_len, vocab_size) + + @traced(span_name="sampling") + def _sample(self, batch_processor: ContinuousBatchProcessor, probs): + if self.do_sample: # sample + probs = nn.functional.softmax(probs, dim=-1) + # probs[0] has shape [seq_len, vocab_size], multinomial returns [seq_len, 1] + next_tokens = torch.multinomial(probs[0], num_samples=1).squeeze(-1) # Now [seq_len] + # Add batch dimension back to match argmax output + next_tokens = next_tokens.unsqueeze(0) # Now [1, seq_len] + else: + next_tokens = torch.argmax(probs, dim=-1) # Already [1, seq_len] + + tokens = next_tokens.size(1) # Get seq_len dimension + batch_processor.output_ids[:, :tokens].copy_(next_tokens) + + def _run_generation_loop(self): + """Main processing loop running in the background thread.""" + batch_processor = None + try: + ref_time = perf_counter() + paged_attention_cache = PagedAttentionCache( + self.model.config, + self.generation_config, + self.model.device, + self.model.dtype, + tp_size=getattr(self.model, "_tp_size", None), # Use model's actual TP setting + ) + logger.debug(f"PagedAttentionCache created in {perf_counter() - ref_time} seconds") + + scheduler = None + if hasattr(self.generation_config, "scheduler"): + scheduler = SCHEDULER_MAPPING.get(self.generation_config.scheduler, None) + if scheduler is None: + logger.warning(f"Scheduler '{scheduler}' not found. Defaulting to FIFO.") + scheduler = FIFOScheduler + else: + # Default to fifo + scheduler = FIFOScheduler + + ref_time = perf_counter() + batch_processor = ContinuousBatchProcessor( + paged_attention_cache, + self.model.config, + self.generation_config, + self.input_queue, + self.output_queue, + self.stop_event, + self.model.device, + self.model.dtype, + scheduler(paged_attention_cache, self.manual_eviction), + self.streaming, + self.manual_eviction, + slice_inputs=self.slice_inputs, + ) + self.batch_processor = batch_processor + self.current_batch = 0 + logger.debug(f"batch_processor created in {perf_counter() - ref_time} seconds") + while (not self.stop_event.is_set()) or batch_processor.has_pending_requests(): + self._inner_generation_loop(batch_processor) + self.current_batch += 1 + + except Exception as e: + logger.error(f"Error in generation loop: {e}", exc_info=True) + self._handle_critical_error(e, batch_processor) + finally: + logger.info("Generation loop finished.") + + @traced(span_name="generation_loop") + def _inner_generation_loop(self, batch_processor: ContinuousBatchProcessor): + if torch.cuda.is_available(): + torch.cuda.synchronize() + if not batch_processor.prepare_next_batch(): + return + if logger.level <= logging.DEBUG: + device, total, reserved, allocated = get_device_and_memory_breakdown() + logger.debug(f"[Memory] Device: {device}, Total: {total}, Reserved: {reserved}, Allocated: {allocated}") + if torch.cuda.is_available() and self.use_cuda_graph: + if self.current_batch == 0: + self.warmup(batch_processor) + elif hasattr(self, "graph"): + try: + self._graph_replay() + except Exception as e: + logger.error(f"Model forward pass failed: {e}", exc_info=True) + batch_processor.handle_batch_error(e) + return + else: + self._generation_step(batch_processor) + else: + self._generation_step(batch_processor) + if torch.cuda.is_available(): + torch.cuda.synchronize() + batch_processor.update_batch() + + @traced(span_name="graph_replay") + def _graph_replay(self): + self.graph.replay() + + @traced + def _handle_critical_error(self, error, batch_processor: Optional[ContinuousBatchProcessor]): + """Handle critical errors that terminate the generation loop.""" + # Signal stop + self.stop_event.set() + + # Fail pending requests in input queue + try: + while True: + req_data = self.input_queue.get_nowait() + if batch_processor is not None: + batch_processor._handle_request_error(error, req_data) + except queue.Empty: + pass + + # Fail active requests + if batch_processor is not None: + batch_processor.fail_all_requests(error) + + @traced + def evict_request_from_cache(self, request_id: str): + """Evict a request from the cache. It is assumed that the request is already finished.""" + if not self.manual_eviction: + raise RuntimeError("Manual eviction is not enabled for this manager.") + if self.batch_processor is not None: + self.batch_processor.scheduler.finish_request(request_id) + + +class ContinuousMixin: + """Mixin class for models to add continuous batching capabilities.""" + + def init_continuous_batching( + self, + generation_config: Optional[GenerationConfig] = None, + manual_eviction: bool = False, + max_queue_size: int = 0, + streaming: bool = False, + slice_inputs: bool = True, + ) -> ContinuousBatchingManager: + """Initialize a manager for continuous batching inference. + + Args: + generation_config: Custom generation configuration + max_queue_size: Maximum size of the input request queue + streaming: Whether to stream tokens as they are generated + + Returns: + `ContinuousBatchingManager`: The manager instance to add requests and retrieve results. + """ + if not hasattr(self, "config") or not hasattr(self, "device") or not hasattr(self, "dtype"): + raise AttributeError("Model must have 'config', 'device', and 'dtype' attributes.") + + gen_config = generation_config if generation_config is not None else self.generation_config + if gen_config is None: + raise ValueError("A GenerationConfig must be provided or set in the model.") + + if gen_config.eos_token_id is None: + logger.warning("`eos_token_id` not set in GenerationConfig. Setting to -1 (disabled).") + gen_config.eos_token_id = -1 + + # Create and return the manager + return ContinuousBatchingManager( + model=self, + generation_config=gen_config, + manual_eviction=manual_eviction, + max_queue_size=max_queue_size, + streaming=streaming, + slice_inputs=slice_inputs, + ) + + @traced + @torch.inference_mode() + def generate_batch( + self, + inputs: list[list[int]], + generation_config: Optional[GenerationConfig] = None, + progress_bar: bool = True, + slice_inputs: bool = True, + **kwargs, + ) -> list[list[int]]: + """Generate sequences for a batch of prompts using continuous batching. + + Args: + inputs: List of input token sequences (prompts) + generation_config: Optional generation configuration + **kwargs: Additional generation parameters + + Returns: + `list[list[int]]`: A list containing the generated sequences (including prompt tokens + if not handled otherwise) for each input prompt, in the same order. + Returns an empty list `[]` for requests that failed. + """ + if not inputs: + return [] + if logger.getEffectiveLevel() <= logging.DEBUG: + logger.warning("Progress bar is disabled when logger level is less than DEBUG") + progress_bar = False + + # Initialize manager with the batch inputs + manager = self.init_continuous_batching(generation_config=generation_config, slice_inputs=slice_inputs) + manager.start() + results = {} + num_requests = len(inputs) + try: + from tqdm.contrib.logging import logging_redirect_tqdm + + with logging_redirect_tqdm([logger]): + with tqdm( + total=num_requests, + disable=(not progress_bar), + desc=f"Solving {num_requests} requests", + unit="request", + ) as pbar: + manager.add_requests(inputs, **kwargs) + finished_count = 0 + while finished_count < num_requests: + result = manager.get_result(timeout=1) + if result: + req_id = result.request_id + if result.status == RequestStatus.FINISHED: + results[req_id] = result + finished_count += 1 + pbar.update(1) + else: + if not manager.is_running(): + logger.error("Generation thread terminated unexpectedly.") + break + + except Exception as e: + logger.error(f"Error during batch generation: {e}", exc_info=True) + finally: + manager.stop(block=True, timeout=5.0) + return results diff --git a/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/requests.py b/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/requests.py new file mode 100644 index 0000000000000000000000000000000000000000..c7842735e7e449eeac7bbc766ed290a3791b5c1f --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/requests.py @@ -0,0 +1,204 @@ +# coding=utf-8 +# Copyright 2025 The HuggingFace Inc. team +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import time +from dataclasses import dataclass, field +from enum import Enum +from typing import Optional + +import torch + +from ...utils.logging import logging +from ...utils.metrics import traced + + +# We centralize the logger here to coordinate between logging and progress bar +logger = logging.getLogger("ContinuousBatchingLogger") +# logger.setLevel(logging.INFO) + + +def get_device_and_memory_breakdown() -> tuple[torch.device, int, int, int]: + if torch.cuda.is_available(): + device = torch.device("cuda") + torch.cuda.empty_cache() + torch.cuda.synchronize() + total_memory = torch.cuda.get_device_properties(device).total_memory + reserved_memory = torch.cuda.memory_reserved(device) + allocated_memory = torch.cuda.memory_allocated(device) + elif torch.backends.mps.is_available() and torch.backends.mps.is_built(): + device = torch.device("mps") + # MPS memory reporting (PyTorch 2.0+) + total_memory = torch.mps.driver_allocated_memory() + allocated_memory = total_memory - torch.mps.recommended_max_memory() + reserved_memory = 0 # MPS does not track reserved separately + else: + device = torch.device("cpu") + total_memory = None + reserved_memory = 0 + allocated_memory = 0 + return device, total_memory, reserved_memory, allocated_memory + + +class RequestStatus(Enum): + """Status of a generation request through its lifecycle.""" + + PENDING = "pending" + PREFILLING = "prefilling" + PREFILLING_SPLIT = "prefilling_split" + SPLIT_PENDING_REMAINDER = "split_pending_remainder" + DECODING = "decoding" + FINISHED = "finished" + FAILED = "failed" + + +@dataclass +class GenerationOutput: + """Tracks the output of a generation request. + + Attributes: + request_id (str): The ID of the generation request. + prompt_ids (list[int]): The IDs of the prompt tokens. + generated_tokens (list[int]): The generated tokens. + logprobs (list[float]): The log probabilities of the generated tokens. + error (Optional[str]): Any error message associated with the request. When None, the request was successful. + status (RequestStatus): The status of the request. + created_time (float): The time the request was created. + """ + + request_id: str + prompt_ids: list[int] = field(default_factory=list) + generated_tokens: list[int] = field(default_factory=list) + logprobs: list[float] = field(default_factory=list) + error: Optional[str] = None + status: RequestStatus = RequestStatus.PENDING + created_time: float = field(default_factory=time.time) + + +@dataclass +class RequestState: + """Tracks the state of a generation request through its lifecycle. + + Attributes: + request_id (str): The ID of the generation request. + full_prompt_ids (list[int] | None): The tokens IDs of the full prompt. + prompt_ids (list[int] | None): The tokens IDs currently being processed. + remaining_prompt_ids (list[int]): The tokens IDs remaining to be processed (for split requests). + static_outputs (list[int]): The generated tokens. + allocated_blocks (int): The number of blocks allocated to the request. + position_offset (int): The current position in the sequence for position_ids. + status (RequestStatus): The status of the request: can be one of PENDING, PREFILLING, PREFILLING_SPLIT, + SPLIT_PENDING_REMAINDER, DECODING, FINISHED, FAILED + max_new_tokens (int): The maximum number of new tokens to generate. + eos_token_id (int): The ID of the end-of-sequence token. + created_time (float): The time the request was created. + error (Optional[str]): Any error message associated with the request. When None, has had no error yet. + """ + + # Required fields + request_id: str + full_prompt_ids: Optional[list[int]] = None # Full initial prompt + prompt_ids: Optional[list[int]] = None # Tokens IDs currently being processed (initial + generated) + remaining_prompt_ids: list[int] = field(default_factory=list) # For split requests, prefill left to process + static_outputs: list[int] = field(default_factory=list) # Generated tokens + allocated_blocks: int = 0 # Number of blocks allocated to the request + position_offset: int = 0 # Current position in the sequence for position_ids + _status: RequestStatus = RequestStatus.PENDING # Status of the request, hidden behind a property + max_new_tokens: int = 20 # Maximum number of new tokens to generate + eos_token_id: int = -1 # ID of the end-of-sequence token + created_time: float = field(default_factory=time.time) # Time the request was created + error: Optional[str] = None # Error message if the request failed + lifespan: tuple[float, float] = (-1, -1) # (time request was no longer pending, time request finished) + + @property + def status(self) -> RequestStatus: + return self._status + + @status.setter + def status(self, value: RequestStatus): + if self._status == RequestStatus.PENDING: + self.lifespan = (time.time(), -1) + elif value == RequestStatus.FINISHED: + self.lifespan = (self.lifespan[0], time.time()) + self.log_end_of_request() + self._status = value + + def log_end_of_request(self): + prefill_len = len(self.full_prompt_ids) + decode_len = self.generated_len() + start_time = self.lifespan[0] - self.created_time + end_time = self.lifespan[1] - self.created_time + logger.info( + f"Request {self.request_id} finished: {prefill_len = } {decode_len = } {start_time = } {end_time = }" + ) + + def current_len(self) -> int: + """Get the current length of the sequence (prompt + generated tokens).""" + return self.position_offset + + def generated_len(self) -> int: + """Get the number of tokens generated so far.""" + return len(self.static_outputs) + + # TODO: this logic seems one token off, check it out + @traced + def update_with_token(self, token_id: int) -> bool: + """Update the request with a newly generated token and check for completion. + + Args: + token_id: The token ID to add to the output sequence + + Returns: + bool: True if the request is now complete, False otherwise + """ + # Only update if we're in decoding state + if self.status != RequestStatus.DECODING: + return False + + is_eos = token_id == self.eos_token_id and self.eos_token_id != -1 + is_max_len = self.generated_len() >= self.max_new_tokens + + # Only add the token if we're not finishing due to max length + # (EOS tokens should still be added to the output) + if not (is_max_len and not is_eos): + self.static_outputs.extend([token_id]) + + if is_eos or is_max_len: + self.status = RequestStatus.FINISHED + return True + return False + + def __repr__(self): + msg = [ + f"request_id={self.request_id}", + f"status={self._status}", + f"out_tokens={self.generated_len()}", + f"query_length={len(self.prompt_ids)}", + f"remaining_tokens={len(self.remaining_prompt_ids)}", + f"kv_length={self.position_offset}", + f"full_prompt_length={len(self.full_prompt_ids)}", + f"allocated_blocks={self.allocated_blocks}", + f"generated_tokens={self.static_outputs}", + ] + return "RequestState(\n\t" + ",\n\t".join(msg) + "\n)" + + def to_generation_output(self): + """Convert the request state to a GenerationOutput object.""" + return GenerationOutput( + request_id=self.request_id, + prompt_ids=self.full_prompt_ids, + status=self.status, + generated_tokens=self.static_outputs, + logprobs=[], + error=self.error, + ) diff --git a/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/scheduler.py b/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/scheduler.py new file mode 100644 index 0000000000000000000000000000000000000000..67cddbf14190a65f367b87bf09cd3f7d323b6c80 --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/generation/continuous_batching/scheduler.py @@ -0,0 +1,300 @@ +# coding=utf-8 +# Copyright 2025 The HuggingFace Inc. team +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import threading +from abc import ABC, abstractmethod +from collections import deque + +from ...utils.metrics import attach_tracer, traced +from .cache import PagedAttentionCache +from .requests import RequestState, RequestStatus + + +class Scheduler(ABC): + """ + Abstract base class for scheduling requests in the continuous batch processor. Schedulers manage the lifecycle of + requests from when they are added to the waiting queue to when they are scheduled for processing. Different + schedulers implement different strategies for prioritizing and batching requests. + """ + + def __init__(self, cache: PagedAttentionCache, retain_cache_on_finish: bool = False): + self.active_requests: dict[str, RequestState] = {} + self.waiting_requests: dict[str, RequestState] = {} + self.waiting_requests_order: deque[str] = deque() + self.cache = cache + self.retain_cache_on_finish = retain_cache_on_finish + self._cancellation_lock = threading.Lock() + self._requests_to_cancel: set[str] = set() + + @traced + def add_waiting_request(self, state: RequestState): + """Adds a request to the waiting list.""" + if self.retain_cache_on_finish and state.request_id in self.active_requests: + old_state = self.active_requests.pop(state.request_id) + state.prompt_ids = state.prompt_ids[len(old_state.full_prompt_ids) :] # XXX: check for indexing error? + state.allocated_blocks = old_state.allocated_blocks + state.position_offset = old_state.position_offset + self.waiting_requests[state.request_id] = state + self.waiting_requests_order.append(state.request_id) + + @abstractmethod + def schedule_batch(self, token_budget: int) -> list[RequestState]: + """Schedules requests for the next batch based on available token budget. This method selects which requests + should be processed in the current batch, considering the token budget and the scheduler's prioritization rules. + The token_budget is the maximum number of tokens that can be processed in this batch.""" + pass + + @traced + def has_pending_requests(self) -> bool: + """Checks if there are requests ready to be processed.""" + return len(self.active_requests) or len(self.waiting_requests) + + @traced + def finish_request(self, request_id: str, evict_from_cache: bool = True): + """Completes processing of a request and optionally frees its allocated cache blocks. This method is called + when a request has finished generation or encountered an error. + """ + if evict_from_cache: + self.cache.free_blocks(request_id) + if request_id in self.active_requests: + del self.active_requests[request_id] + + @traced + def get_active_request_static_outputs(self, request_id: str) -> list[int]: + """Gets generated tokens for an active request.""" + if request_id in self.active_requests: + return self.active_requests[request_id].static_outputs + return [] + + @traced + def set_request_cancellation(self, request_id: str): + """Marks a request for cancellation.""" + with self._cancellation_lock: + self._requests_to_cancel.add(request_id) + + @traced + def clear_cancelled_requests(self): + """Remove all cancelled requests from active and waiting queues.""" + with self._cancellation_lock: + for request_id in self._requests_to_cancel: + if request_id in self.active_requests: + del self.active_requests[request_id] + if request_id in self.waiting_requests: + del self.waiting_requests[request_id] + if request_id in self.waiting_requests_order: + self.waiting_requests_order.remove(request_id) + self.cache.free_blocks(request_id) + self._requests_to_cancel = set() + + @traced + def request_is_cancelled(self, request_id: str) -> bool: + """Checks if a request has been cancelled or removed.""" + return request_id in self._requests_to_cancel or ( + request_id not in self.active_requests and request_id not in self.waiting_requests + ) + + @traced + def _allocate_blocks_if_needed(self, state: RequestState, len_next_tokens: int) -> bool: + """Allocate additional cache blocks for a request if the currently allocated blocks are insufficient to + accommodate the next tokens. It calculates how many blocks are needed based on the request's current + cache occupancy and the number of tokens to be processed. The allocation itself is done by the CacheAllocator + objects. Returns a boolean indicating if the allocation was successful or not. + """ + # 1. we check that the occupancy is less than the requested length + # 2. we allocate enough blocks to cover the requested length + current_len = state.current_len() + occupancy = state.allocated_blocks * self.cache.block_size - current_len + if occupancy < len_next_tokens or state.allocated_blocks == 0: + blocks_needed = ((len_next_tokens - occupancy + 1) // self.cache.block_size) + 1 + allocated = self.cache.allocate_blocks(blocks_needed, state.request_id) + if allocated is None: + return False + state.allocated_blocks += allocated + return True + + @traced(span_name="prepare_request") + def _prepare_request_for_processing( + self, state: RequestState, token_budget: int, request_ids_to_remove_from_waiting: set[str] + ): + """Prepares a request for processing in the current batch.""" + request_tokens = ( + state.remaining_prompt_ids if state.status == RequestStatus.SPLIT_PENDING_REMAINDER else state.prompt_ids + ) + if len(request_tokens) < token_budget: + # Can process the entire prompt/remainder + if state.status == RequestStatus.PENDING: + self.active_requests[state.request_id] = state + state.status = RequestStatus.PREFILLING + request_ids_to_remove_from_waiting.add(state.request_id) + elif state.status == RequestStatus.SPLIT_PENDING_REMAINDER: + state.status = RequestStatus.PREFILLING + state.prompt_ids = state.remaining_prompt_ids + state.remaining_prompt_ids = [] + else: + # Need to split the request + if state.status == RequestStatus.PENDING: + self.active_requests[state.request_id] = state + state.status = RequestStatus.PREFILLING_SPLIT + request_ids_to_remove_from_waiting.add(state.request_id) + elif state.status == RequestStatus.SPLIT_PENDING_REMAINDER: + state.status = RequestStatus.PREFILLING_SPLIT + state.remaining_prompt_ids = request_tokens[token_budget:] + state.prompt_ids = request_tokens[:token_budget] + + +@attach_tracer() +class FIFOScheduler(Scheduler): + """This scheduler processes requests in the order they arrive, meaning decoding requests has priority over + prefilling requests. Additionally, it includes a safety margin mechanism to prevent cache exhaustion. By default, + when 80% of the cache is full, new requests will not be scheduled to prioritize decoding active requests.""" + + def __init__(self, cache: PagedAttentionCache, retain_cache_on_finish: bool = False, safety_margin: float = 0.2): + """Initializes the FIFO scheduler. The safety margin is the percentage of free blocks under which we stop + scheduling new prefill requests, so safety_margin = 0.1 means that when there is less than 10% of free blocks, + or equivalently when more than 90% of blocks are already allocated, we stop scheduling new prefill requests. + """ + super().__init__(cache, retain_cache_on_finish) + self.safety_margin = safety_margin + + @traced + def schedule_batch(self, token_budget: int) -> list[RequestState]: + priority_states: list[RequestState] = [] + second_priority_states: list[RequestState] = [] + scheduled_requests = [] + + for state in self.active_requests.values(): + if state.status == RequestStatus.DECODING: + priority_states.append(state) + if state.status in [RequestStatus.SPLIT_PENDING_REMAINDER, RequestStatus.PREFILLING_SPLIT]: + second_priority_states.append(state) + + # Add waiting requests to second priority + for req_id in self.waiting_requests_order: + second_priority_states.append(self.waiting_requests[req_id]) + + candidates = priority_states + second_priority_states + request_ids_to_remove_from_waiting = set() + safety_margins = self.safety_margin * self.cache.num_blocks + + for state in candidates: + # If we are out the safety margin, we only accept decoding requests or the first prefill request + num_free_blocks = self.cache.get_num_free_blocks() + outside_safety_margin = num_free_blocks < safety_margins + if outside_safety_margin and scheduled_requests and state.status != RequestStatus.DECODING: + break + + self._prepare_request_for_processing(state, token_budget, request_ids_to_remove_from_waiting) + request_len = len(state.prompt_ids) + if not self._allocate_blocks_if_needed( + state, len(state.prompt_ids) + ): # don't schedule if we can't allocate blocks + if len(self.cache._free_blocks) == 0: + break + continue + + @traced + def _add_to_scheduled_requests(state: RequestState): + scheduled_requests.append(state) + + _add_to_scheduled_requests(state) + + token_budget -= request_len + + @traced + def _remove_from_waiting_requests(state: RequestState): + req_id = state.request_id + if req_id in self.waiting_requests: + del self.waiting_requests[req_id] + request_ids_to_remove_from_waiting.add(req_id) + + _remove_from_waiting_requests(state) + + if token_budget == 0: + break + + self.waiting_requests_order = deque( + [req_id for req_id in self.waiting_requests_order if req_id not in request_ids_to_remove_from_waiting] + ) + + return scheduled_requests + + +# FIXME: prioritize adding from waiting reqs before scheduling `RequestStatus.DECODING` when cache space allows it +@attach_tracer() +class PrefillFirstScheduler(Scheduler): + """Scheduler that prioritizes split prefill requests over decoding requests. This scheduler ensures that split + prefill requests (which are continuations of partially processed prompts) are completed before processing new + decoding requests.""" + + @traced + def schedule_batch(self, token_budget: int) -> list[RequestState]: + priority_states: list[RequestState] = [] + second_priority_states: list[RequestState] = [] + scheduled_requests = [] + + for state in self.active_requests.values(): + # XXX: when cache is full, state can stay on `PREFILLING_SPLIT` so we need to take those into account + if state.status in [RequestStatus.PREFILLING_SPLIT, RequestStatus.SPLIT_PENDING_REMAINDER]: + priority_states.append(state) + elif state.status == RequestStatus.DECODING: + second_priority_states.append(state) + + for req_id in self.waiting_requests_order: + second_priority_states.append(self.waiting_requests[req_id]) + + candidates = priority_states + second_priority_states + + request_ids_to_remove_from_waiting = set() + + for state in candidates: + self._prepare_request_for_processing(state, token_budget, request_ids_to_remove_from_waiting) + request_len = len(state.prompt_ids) + if not self._allocate_blocks_if_needed( + state, len(state.prompt_ids) + ): # don't schedule if we can't allocate blocks + if len(self.cache._free_blocks) == 0: + break + continue + + @traced + def _add_to_scheduled_requests(state: RequestState): + scheduled_requests.append(state) + + _add_to_scheduled_requests(state) + + token_budget -= request_len + + @traced + def _remove_from_waiting_requests(state: RequestState): + req_id = state.request_id + if req_id in self.waiting_requests: + del self.waiting_requests[req_id] + request_ids_to_remove_from_waiting.add(req_id) + + _remove_from_waiting_requests(state) + + if token_budget == 0: + break + + self.waiting_requests_order = deque( + [req_id for req_id in self.waiting_requests_order if req_id not in request_ids_to_remove_from_waiting] + ) + + return scheduled_requests + + +SCHEDULER_MAPPING = { + "fifo": FIFOScheduler, + "prefill_first": PrefillFirstScheduler, +} diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4eb4f340400f0c00230eccb30b4602390e1b2c79 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/accelerate.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/accelerate.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f3d94048e0a2f4e8e673ab1c5aa8136b46d38d0e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/accelerate.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/aqlm.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/aqlm.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0b87f3266ea648488c8db6a80de1dbb5a284d089 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/aqlm.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/awq.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/awq.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6be6987d2038a28410a5372042cc23af7a3abc27 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/awq.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/bitnet.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/bitnet.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..17eaa96e6a08bff41b980e291ae0273a7a176f11 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/bitnet.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/bitsandbytes.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/bitsandbytes.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fff0c17c76169219d08828ae03f2a476e76866a3 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/bitsandbytes.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/deepspeed.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/deepspeed.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2a9858da46c3be418cff541dc8bfbca20e1e8a6a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/deepspeed.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/eager_paged.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/eager_paged.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..df13048b63e00382bfa92a14688d2273f361874d Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/eager_paged.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/eetq.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/eetq.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dd11d0f858de85b7e96a94c1057c26a2a8ed3eec Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/eetq.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/executorch.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/executorch.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ec2d24b329ee589893a1cfaa5c040a1fadbbbf0a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/executorch.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/fbgemm_fp8.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/fbgemm_fp8.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a44640f90e1533729bdf713aa0f4b7ad02b2b109 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/fbgemm_fp8.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/finegrained_fp8.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/finegrained_fp8.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9f867896e199b97842091bcd153728696306cc65 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/finegrained_fp8.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/flash_attention.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/flash_attention.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5a5642c8fe4ef4187eb8257f737be8eb7eac1679 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/flash_attention.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/flash_paged.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/flash_paged.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f4e85bc2a4b520266d9ec9666f87aed8cc45842a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/flash_paged.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/flex_attention.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/flex_attention.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1102ba22d53e805f09913f5a700f0a7750e1a777 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/flex_attention.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/fp_quant.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/fp_quant.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6cb1bef4638c2b9a43e899b65d235825528ebdf9 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/fp_quant.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/fsdp.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/fsdp.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3d4128db24e80ef8930db36d1046549cc0c47284 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/fsdp.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/ggml.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/ggml.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8c825b10c9711b9a320e7076d42ec3abdfd484fa Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/ggml.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/higgs.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/higgs.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a79123aae470620e2cc888cd6f5a955574c50ad0 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/higgs.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/hqq.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/hqq.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9f867785f5735179f176269a261d884b49f23daa Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/hqq.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/hub_kernels.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/hub_kernels.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c300f72f43802e14fcb4d7555b5f94d75849b434 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/hub_kernels.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/mistral.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/mistral.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bcaa18d43e49bbe54acdec0d446678f504835ed0 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/mistral.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/mxfp4.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/mxfp4.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5612aff5a49d50db495c76a04c15b8b5bd33d3bc Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/mxfp4.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/npu_flash_attention.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/npu_flash_attention.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f77cf840696f00a579aecf68cea7e39ab5fd8b76 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/npu_flash_attention.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/peft.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/peft.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2b939838c3004b45159775472bee58497695289f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/peft.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/quanto.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/quanto.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8cea352a20b80e24f96e909a929711cb65bd38aa Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/quanto.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/sdpa_attention.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/sdpa_attention.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..85a5eed273c94995f32685bea11ca7bbc655988a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/sdpa_attention.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/sdpa_paged.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/sdpa_paged.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a61d1d96473eac6b2fa1124812ab8c00cb8c9f7a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/sdpa_paged.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/spqr.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/spqr.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a92087d03c032c0a417065f305ae933d462e4077 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/spqr.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/tensor_parallel.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/tensor_parallel.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..32cc8655693aaebf223dd49c72a3260b3f9d62cc Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/tensor_parallel.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/tiktoken.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/tiktoken.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1c317646cbdacf98e9c9723a94c1c05da93d734a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/tiktoken.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/tpu.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/tpu.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bad22c7c6ec29f458a18bd226c6fbd812bcb592e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/tpu.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/vptq.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/vptq.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c2a9dfec9442fb1cee6a8caaee8a0e93e3c4a11f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/integrations/__pycache__/vptq.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/kernels/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/kernels/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8b4fa0f2bb8fdf19c39923514a945323423189f7 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/kernels/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/kernels/deta/cpu/ms_deform_attn_cpu.cpp b/venv/lib/python3.13/site-packages/transformers/kernels/deta/cpu/ms_deform_attn_cpu.cpp new file mode 100644 index 0000000000000000000000000000000000000000..388a73d22d4c9b561e2a887b50a1897b8cf2def9 --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/kernels/deta/cpu/ms_deform_attn_cpu.cpp @@ -0,0 +1,40 @@ +/*! +************************************************************************************************** +* Deformable DETR +* Copyright (c) 2020 SenseTime. All Rights Reserved. +* Licensed under the Apache License, Version 2.0 [see LICENSE for details] +************************************************************************************************** +* Modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0 +************************************************************************************************** +*/ + +#include + +#include +#include + + +at::Tensor +ms_deform_attn_cpu_forward( + const at::Tensor &value, + const at::Tensor &spatial_shapes, + const at::Tensor &level_start_index, + const at::Tensor &sampling_loc, + const at::Tensor &attn_weight, + const int im2col_step) +{ + AT_ERROR("Not implement on cpu"); +} + +std::vector +ms_deform_attn_cpu_backward( + const at::Tensor &value, + const at::Tensor &spatial_shapes, + const at::Tensor &level_start_index, + const at::Tensor &sampling_loc, + const at::Tensor &attn_weight, + const at::Tensor &grad_output, + const int im2col_step) +{ + AT_ERROR("Not implement on cpu"); +} diff --git a/venv/lib/python3.13/site-packages/transformers/kernels/deta/cpu/ms_deform_attn_cpu.h b/venv/lib/python3.13/site-packages/transformers/kernels/deta/cpu/ms_deform_attn_cpu.h new file mode 100644 index 0000000000000000000000000000000000000000..7eac8c8bcd1bf529bb9c13d54d2d4215c9e4c89f --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/kernels/deta/cpu/ms_deform_attn_cpu.h @@ -0,0 +1,32 @@ +/*! +************************************************************************************************** +* Deformable DETR +* Copyright (c) 2020 SenseTime. All Rights Reserved. +* Licensed under the Apache License, Version 2.0 [see LICENSE for details] +************************************************************************************************** +* Modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0 +************************************************************************************************** +*/ + +#pragma once +#include + +at::Tensor +ms_deform_attn_cpu_forward( + const at::Tensor &value, + const at::Tensor &spatial_shapes, + const at::Tensor &level_start_index, + const at::Tensor &sampling_loc, + const at::Tensor &attn_weight, + const int im2col_step); + +std::vector +ms_deform_attn_cpu_backward( + const at::Tensor &value, + const at::Tensor &spatial_shapes, + const at::Tensor &level_start_index, + const at::Tensor &sampling_loc, + const at::Tensor &attn_weight, + const at::Tensor &grad_output, + const int im2col_step); + diff --git a/venv/lib/python3.13/site-packages/transformers/kernels/deta/cuda/ms_deform_attn_cuda.cu b/venv/lib/python3.13/site-packages/transformers/kernels/deta/cuda/ms_deform_attn_cuda.cu new file mode 100644 index 0000000000000000000000000000000000000000..8ea1d7fabe2684dbb85f00fae2c47b469687cb2c --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/kernels/deta/cuda/ms_deform_attn_cuda.cu @@ -0,0 +1,156 @@ +/*! +************************************************************************************************** +* Deformable DETR +* Copyright (c) 2020 SenseTime. All Rights Reserved. +* Licensed under the Apache License, Version 2.0 [see LICENSE for details] +************************************************************************************************** +* Modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0 +************************************************************************************************** +*/ + +#include +#include "cuda/ms_deform_im2col_cuda.cuh" + +#include +#include +#include +#include + +#pragma once +#include + + +at::Tensor ms_deform_attn_cuda_forward( + const at::Tensor &value, + const at::Tensor &spatial_shapes, + const at::Tensor &level_start_index, + const at::Tensor &sampling_loc, + const at::Tensor &attn_weight, + const int im2col_step) +{ + AT_ASSERTM(value.is_contiguous(), "value tensor has to be contiguous"); + AT_ASSERTM(spatial_shapes.is_contiguous(), "spatial_shapes tensor has to be contiguous"); + AT_ASSERTM(level_start_index.is_contiguous(), "level_start_index tensor has to be contiguous"); + AT_ASSERTM(sampling_loc.is_contiguous(), "sampling_loc tensor has to be contiguous"); + AT_ASSERTM(attn_weight.is_contiguous(), "attn_weight tensor has to be contiguous"); + + AT_ASSERTM(value.type().is_cuda(), "value must be a CUDA tensor"); + AT_ASSERTM(spatial_shapes.type().is_cuda(), "spatial_shapes must be a CUDA tensor"); + AT_ASSERTM(level_start_index.type().is_cuda(), "level_start_index must be a CUDA tensor"); + AT_ASSERTM(sampling_loc.type().is_cuda(), "sampling_loc must be a CUDA tensor"); + AT_ASSERTM(attn_weight.type().is_cuda(), "attn_weight must be a CUDA tensor"); + + const int batch = value.size(0); + const int spatial_size = value.size(1); + const int num_heads = value.size(2); + const int channels = value.size(3); + + const int num_levels = spatial_shapes.size(0); + + const int num_query = sampling_loc.size(1); + const int num_point = sampling_loc.size(4); + + const int im2col_step_ = std::min(batch, im2col_step); + + AT_ASSERTM(batch % im2col_step_ == 0, "batch(%d) must divide im2col_step(%d)", batch, im2col_step_); + + auto output = at::zeros({batch, num_query, num_heads, channels}, value.options()); + + const int batch_n = im2col_step_; + auto output_n = output.view({batch/im2col_step_, batch_n, num_query, num_heads, channels}); + auto per_value_size = spatial_size * num_heads * channels; + auto per_sample_loc_size = num_query * num_heads * num_levels * num_point * 2; + auto per_attn_weight_size = num_query * num_heads * num_levels * num_point; + for (int n = 0; n < batch/im2col_step_; ++n) + { + auto columns = output_n.select(0, n); + AT_DISPATCH_FLOATING_TYPES(value.type(), "ms_deform_attn_forward_cuda", ([&] { + ms_deformable_im2col_cuda(at::cuda::getCurrentCUDAStream(), + value.data() + n * im2col_step_ * per_value_size, + spatial_shapes.data(), + level_start_index.data(), + sampling_loc.data() + n * im2col_step_ * per_sample_loc_size, + attn_weight.data() + n * im2col_step_ * per_attn_weight_size, + batch_n, spatial_size, num_heads, channels, num_levels, num_query, num_point, + columns.data()); + + })); + } + + output = output.view({batch, num_query, num_heads*channels}); + + return output; +} + + +std::vector ms_deform_attn_cuda_backward( + const at::Tensor &value, + const at::Tensor &spatial_shapes, + const at::Tensor &level_start_index, + const at::Tensor &sampling_loc, + const at::Tensor &attn_weight, + const at::Tensor &grad_output, + const int im2col_step) +{ + + AT_ASSERTM(value.is_contiguous(), "value tensor has to be contiguous"); + AT_ASSERTM(spatial_shapes.is_contiguous(), "spatial_shapes tensor has to be contiguous"); + AT_ASSERTM(level_start_index.is_contiguous(), "level_start_index tensor has to be contiguous"); + AT_ASSERTM(sampling_loc.is_contiguous(), "sampling_loc tensor has to be contiguous"); + AT_ASSERTM(attn_weight.is_contiguous(), "attn_weight tensor has to be contiguous"); + AT_ASSERTM(grad_output.is_contiguous(), "grad_output tensor has to be contiguous"); + + AT_ASSERTM(value.type().is_cuda(), "value must be a CUDA tensor"); + AT_ASSERTM(spatial_shapes.type().is_cuda(), "spatial_shapes must be a CUDA tensor"); + AT_ASSERTM(level_start_index.type().is_cuda(), "level_start_index must be a CUDA tensor"); + AT_ASSERTM(sampling_loc.type().is_cuda(), "sampling_loc must be a CUDA tensor"); + AT_ASSERTM(attn_weight.type().is_cuda(), "attn_weight must be a CUDA tensor"); + AT_ASSERTM(grad_output.type().is_cuda(), "grad_output must be a CUDA tensor"); + + const int batch = value.size(0); + const int spatial_size = value.size(1); + const int num_heads = value.size(2); + const int channels = value.size(3); + + const int num_levels = spatial_shapes.size(0); + + const int num_query = sampling_loc.size(1); + const int num_point = sampling_loc.size(4); + + const int im2col_step_ = std::min(batch, im2col_step); + + AT_ASSERTM(batch % im2col_step_ == 0, "batch(%d) must divide im2col_step(%d)", batch, im2col_step_); + + auto grad_value = at::zeros_like(value); + auto grad_sampling_loc = at::zeros_like(sampling_loc); + auto grad_attn_weight = at::zeros_like(attn_weight); + + const int batch_n = im2col_step_; + auto per_value_size = spatial_size * num_heads * channels; + auto per_sample_loc_size = num_query * num_heads * num_levels * num_point * 2; + auto per_attn_weight_size = num_query * num_heads * num_levels * num_point; + auto grad_output_n = grad_output.view({batch/im2col_step_, batch_n, num_query, num_heads, channels}); + + for (int n = 0; n < batch/im2col_step_; ++n) + { + auto grad_output_g = grad_output_n.select(0, n); + AT_DISPATCH_FLOATING_TYPES(value.type(), "ms_deform_attn_backward_cuda", ([&] { + ms_deformable_col2im_cuda(at::cuda::getCurrentCUDAStream(), + grad_output_g.data(), + value.data() + n * im2col_step_ * per_value_size, + spatial_shapes.data(), + level_start_index.data(), + sampling_loc.data() + n * im2col_step_ * per_sample_loc_size, + attn_weight.data() + n * im2col_step_ * per_attn_weight_size, + batch_n, spatial_size, num_heads, channels, num_levels, num_query, num_point, + grad_value.data() + n * im2col_step_ * per_value_size, + grad_sampling_loc.data() + n * im2col_step_ * per_sample_loc_size, + grad_attn_weight.data() + n * im2col_step_ * per_attn_weight_size); + + })); + } + + return { + grad_value, grad_sampling_loc, grad_attn_weight + }; +} diff --git a/venv/lib/python3.13/site-packages/transformers/kernels/deta/cuda/ms_deform_attn_cuda.cuh b/venv/lib/python3.13/site-packages/transformers/kernels/deta/cuda/ms_deform_attn_cuda.cuh new file mode 100644 index 0000000000000000000000000000000000000000..34f8ae9cb77bbaa8cb4dd25e0cb86632db9ad05d --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/kernels/deta/cuda/ms_deform_attn_cuda.cuh @@ -0,0 +1,1467 @@ +/*! +************************************************************************************************** +* Deformable DETR +* Copyright (c) 2020 SenseTime. All Rights Reserved. +* Licensed under the Apache License, Version 2.0 [see LICENSE for details] +************************************************************************************************** +* Modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0 +************************************************************************************************** +*/ + +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include + +#define CUDA_KERNEL_LOOP(i, n) \ + for (int i = blockIdx.x * blockDim.x + threadIdx.x; \ + i < (n); \ + i += blockDim.x * gridDim.x) + + +at::Tensor ms_deform_attn_cuda_forward( + const at::Tensor &value, + const at::Tensor &spatial_shapes, + const at::Tensor &level_start_index, + const at::Tensor &sampling_loc, + const at::Tensor &attn_weight, + const int im2col_step) +{ + AT_ASSERTM(value.is_contiguous(), "value tensor has to be contiguous"); + AT_ASSERTM(spatial_shapes.is_contiguous(), "spatial_shapes tensor has to be contiguous"); + AT_ASSERTM(level_start_index.is_contiguous(), "level_start_index tensor has to be contiguous"); + AT_ASSERTM(sampling_loc.is_contiguous(), "sampling_loc tensor has to be contiguous"); + AT_ASSERTM(attn_weight.is_contiguous(), "attn_weight tensor has to be contiguous"); + + AT_ASSERTM(value.type().is_cuda(), "value must be a CUDA tensor"); + AT_ASSERTM(spatial_shapes.type().is_cuda(), "spatial_shapes must be a CUDA tensor"); + AT_ASSERTM(level_start_index.type().is_cuda(), "level_start_index must be a CUDA tensor"); + AT_ASSERTM(sampling_loc.type().is_cuda(), "sampling_loc must be a CUDA tensor"); + AT_ASSERTM(attn_weight.type().is_cuda(), "attn_weight must be a CUDA tensor"); + + const int batch = value.size(0); + const int spatial_size = value.size(1); + const int num_heads = value.size(2); + const int channels = value.size(3); + + const int num_levels = spatial_shapes.size(0); + + const int num_query = sampling_loc.size(1); + const int num_point = sampling_loc.size(4); + + const int im2col_step_ = std::min(batch, im2col_step); + + AT_ASSERTM(batch % im2col_step_ == 0, "batch(%d) must divide im2col_step(%d)", batch, im2col_step_); + + auto output = at::zeros({batch, num_query, num_heads, channels}, value.options()); + + const int batch_n = im2col_step_; + auto output_n = output.view({batch/im2col_step_, batch_n, num_query, num_heads, channels}); + auto per_value_size = spatial_size * num_heads * channels; + auto per_sample_loc_size = num_query * num_heads * num_levels * num_point * 2; + auto per_attn_weight_size = num_query * num_heads * num_levels * num_point; + for (int n = 0; n < batch/im2col_step_; ++n) + { + auto columns = output_n.select(0, n); + AT_DISPATCH_FLOATING_TYPES(value.type(), "ms_deform_attn_forward_cuda", ([&] { + ms_deformable_im2col_cuda(at::cuda::getCurrentCUDAStream(), + value.data() + n * im2col_step_ * per_value_size, + spatial_shapes.data(), + level_start_index.data(), + sampling_loc.data() + n * im2col_step_ * per_sample_loc_size, + attn_weight.data() + n * im2col_step_ * per_attn_weight_size, + batch_n, spatial_size, num_heads, channels, num_levels, num_query, num_point, + columns.data()); + + })); + } + + output = output.view({batch, num_query, num_heads*channels}); + + return output; +} + + +std::vector ms_deform_attn_cuda_backward( + const at::Tensor &value, + const at::Tensor &spatial_shapes, + const at::Tensor &level_start_index, + const at::Tensor &sampling_loc, + const at::Tensor &attn_weight, + const at::Tensor &grad_output, + const int im2col_step) +{ + + AT_ASSERTM(value.is_contiguous(), "value tensor has to be contiguous"); + AT_ASSERTM(spatial_shapes.is_contiguous(), "spatial_shapes tensor has to be contiguous"); + AT_ASSERTM(level_start_index.is_contiguous(), "level_start_index tensor has to be contiguous"); + AT_ASSERTM(sampling_loc.is_contiguous(), "sampling_loc tensor has to be contiguous"); + AT_ASSERTM(attn_weight.is_contiguous(), "attn_weight tensor has to be contiguous"); + AT_ASSERTM(grad_output.is_contiguous(), "grad_output tensor has to be contiguous"); + + AT_ASSERTM(value.type().is_cuda(), "value must be a CUDA tensor"); + AT_ASSERTM(spatial_shapes.type().is_cuda(), "spatial_shapes must be a CUDA tensor"); + AT_ASSERTM(level_start_index.type().is_cuda(), "level_start_index must be a CUDA tensor"); + AT_ASSERTM(sampling_loc.type().is_cuda(), "sampling_loc must be a CUDA tensor"); + AT_ASSERTM(attn_weight.type().is_cuda(), "attn_weight must be a CUDA tensor"); + AT_ASSERTM(grad_output.type().is_cuda(), "grad_output must be a CUDA tensor"); + + const int batch = value.size(0); + const int spatial_size = value.size(1); + const int num_heads = value.size(2); + const int channels = value.size(3); + + const int num_levels = spatial_shapes.size(0); + + const int num_query = sampling_loc.size(1); + const int num_point = sampling_loc.size(4); + + const int im2col_step_ = std::min(batch, im2col_step); + + AT_ASSERTM(batch % im2col_step_ == 0, "batch(%d) must divide im2col_step(%d)", batch, im2col_step_); + + auto grad_value = at::zeros_like(value); + auto grad_sampling_loc = at::zeros_like(sampling_loc); + auto grad_attn_weight = at::zeros_like(attn_weight); + + const int batch_n = im2col_step_; + auto per_value_size = spatial_size * num_heads * channels; + auto per_sample_loc_size = num_query * num_heads * num_levels * num_point * 2; + auto per_attn_weight_size = num_query * num_heads * num_levels * num_point; + auto grad_output_n = grad_output.view({batch/im2col_step_, batch_n, num_query, num_heads, channels}); + + for (int n = 0; n < batch/im2col_step_; ++n) + { + auto grad_output_g = grad_output_n.select(0, n); + AT_DISPATCH_FLOATING_TYPES(value.type(), "ms_deform_attn_backward_cuda", ([&] { + ms_deformable_col2im_cuda(at::cuda::getCurrentCUDAStream(), + grad_output_g.data(), + value.data() + n * im2col_step_ * per_value_size, + spatial_shapes.data(), + level_start_index.data(), + sampling_loc.data() + n * im2col_step_ * per_sample_loc_size, + attn_weight.data() + n * im2col_step_ * per_attn_weight_size, + batch_n, spatial_size, num_heads, channels, num_levels, num_query, num_point, + grad_value.data() + n * im2col_step_ * per_value_size, + grad_sampling_loc.data() + n * im2col_step_ * per_sample_loc_size, + grad_attn_weight.data() + n * im2col_step_ * per_attn_weight_size); + + })); + } + + return { + grad_value, grad_sampling_loc, grad_attn_weight + }; +} + +const int CUDA_NUM_THREADS = 1024; +inline int GET_BLOCKS(const int N, const int num_threads) +{ + return (N + num_threads - 1) / num_threads; +} + + +template +__device__ scalar_t ms_deform_attn_im2col_bilinear(const scalar_t* &bottom_data, + const int &height, const int &width, const int &nheads, const int &channels, + const scalar_t &h, const scalar_t &w, const int &m, const int &c) +{ + const int h_low = floor(h); + const int w_low = floor(w); + const int h_high = h_low + 1; + const int w_high = w_low + 1; + + const scalar_t lh = h - h_low; + const scalar_t lw = w - w_low; + const scalar_t hh = 1 - lh, hw = 1 - lw; + + const int w_stride = nheads * channels; + const int h_stride = width * w_stride; + const int h_low_ptr_offset = h_low * h_stride; + const int h_high_ptr_offset = h_low_ptr_offset + h_stride; + const int w_low_ptr_offset = w_low * w_stride; + const int w_high_ptr_offset = w_low_ptr_offset + w_stride; + const int base_ptr = m * channels + c; + + scalar_t v1 = 0; + if (h_low >= 0 && w_low >= 0) + { + const int ptr1 = h_low_ptr_offset + w_low_ptr_offset + base_ptr; + v1 = bottom_data[ptr1]; + } + scalar_t v2 = 0; + if (h_low >= 0 && w_high <= width - 1) + { + const int ptr2 = h_low_ptr_offset + w_high_ptr_offset + base_ptr; + v2 = bottom_data[ptr2]; + } + scalar_t v3 = 0; + if (h_high <= height - 1 && w_low >= 0) + { + const int ptr3 = h_high_ptr_offset + w_low_ptr_offset + base_ptr; + v3 = bottom_data[ptr3]; + } + scalar_t v4 = 0; + if (h_high <= height - 1 && w_high <= width - 1) + { + const int ptr4 = h_high_ptr_offset + w_high_ptr_offset + base_ptr; + v4 = bottom_data[ptr4]; + } + + const scalar_t w1 = hh * hw, w2 = hh * lw, w3 = lh * hw, w4 = lh * lw; + + const scalar_t val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); + return val; +} + + +template +__device__ void ms_deform_attn_col2im_bilinear(const scalar_t* &bottom_data, + const int &height, const int &width, const int &nheads, const int &channels, + const scalar_t &h, const scalar_t &w, const int &m, const int &c, + const scalar_t &top_grad, + const scalar_t &attn_weight, + scalar_t* &grad_value, + scalar_t* grad_sampling_loc, + scalar_t* grad_attn_weight) +{ + const int h_low = floor(h); + const int w_low = floor(w); + const int h_high = h_low + 1; + const int w_high = w_low + 1; + + const scalar_t lh = h - h_low; + const scalar_t lw = w - w_low; + const scalar_t hh = 1 - lh, hw = 1 - lw; + + const int w_stride = nheads * channels; + const int h_stride = width * w_stride; + const int h_low_ptr_offset = h_low * h_stride; + const int h_high_ptr_offset = h_low_ptr_offset + h_stride; + const int w_low_ptr_offset = w_low * w_stride; + const int w_high_ptr_offset = w_low_ptr_offset + w_stride; + const int base_ptr = m * channels + c; + + const scalar_t w1 = hh * hw, w2 = hh * lw, w3 = lh * hw, w4 = lh * lw; + const scalar_t top_grad_value = top_grad * attn_weight; + scalar_t grad_h_weight = 0, grad_w_weight = 0; + + scalar_t v1 = 0; + if (h_low >= 0 && w_low >= 0) + { + const int ptr1 = h_low_ptr_offset + w_low_ptr_offset + base_ptr; + v1 = bottom_data[ptr1]; + grad_h_weight -= hw * v1; + grad_w_weight -= hh * v1; + atomicAdd(grad_value+ptr1, w1*top_grad_value); + } + scalar_t v2 = 0; + if (h_low >= 0 && w_high <= width - 1) + { + const int ptr2 = h_low_ptr_offset + w_high_ptr_offset + base_ptr; + v2 = bottom_data[ptr2]; + grad_h_weight -= lw * v2; + grad_w_weight += hh * v2; + atomicAdd(grad_value+ptr2, w2*top_grad_value); + } + scalar_t v3 = 0; + if (h_high <= height - 1 && w_low >= 0) + { + const int ptr3 = h_high_ptr_offset + w_low_ptr_offset + base_ptr; + v3 = bottom_data[ptr3]; + grad_h_weight += hw * v3; + grad_w_weight -= lh * v3; + atomicAdd(grad_value+ptr3, w3*top_grad_value); + } + scalar_t v4 = 0; + if (h_high <= height - 1 && w_high <= width - 1) + { + const int ptr4 = h_high_ptr_offset + w_high_ptr_offset + base_ptr; + v4 = bottom_data[ptr4]; + grad_h_weight += lw * v4; + grad_w_weight += lh * v4; + atomicAdd(grad_value+ptr4, w4*top_grad_value); + } + + const scalar_t val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); + *grad_attn_weight = top_grad * val; + *grad_sampling_loc = width * grad_w_weight * top_grad_value; + *(grad_sampling_loc + 1) = height * grad_h_weight * top_grad_value; +} + + +template +__device__ void ms_deform_attn_col2im_bilinear_gm(const scalar_t* &bottom_data, + const int &height, const int &width, const int &nheads, const int &channels, + const scalar_t &h, const scalar_t &w, const int &m, const int &c, + const scalar_t &top_grad, + const scalar_t &attn_weight, + scalar_t* &grad_value, + scalar_t* grad_sampling_loc, + scalar_t* grad_attn_weight) +{ + const int h_low = floor(h); + const int w_low = floor(w); + const int h_high = h_low + 1; + const int w_high = w_low + 1; + + const scalar_t lh = h - h_low; + const scalar_t lw = w - w_low; + const scalar_t hh = 1 - lh, hw = 1 - lw; + + const int w_stride = nheads * channels; + const int h_stride = width * w_stride; + const int h_low_ptr_offset = h_low * h_stride; + const int h_high_ptr_offset = h_low_ptr_offset + h_stride; + const int w_low_ptr_offset = w_low * w_stride; + const int w_high_ptr_offset = w_low_ptr_offset + w_stride; + const int base_ptr = m * channels + c; + + const scalar_t w1 = hh * hw, w2 = hh * lw, w3 = lh * hw, w4 = lh * lw; + const scalar_t top_grad_value = top_grad * attn_weight; + scalar_t grad_h_weight = 0, grad_w_weight = 0; + + scalar_t v1 = 0; + if (h_low >= 0 && w_low >= 0) + { + const int ptr1 = h_low_ptr_offset + w_low_ptr_offset + base_ptr; + v1 = bottom_data[ptr1]; + grad_h_weight -= hw * v1; + grad_w_weight -= hh * v1; + atomicAdd(grad_value+ptr1, w1*top_grad_value); + } + scalar_t v2 = 0; + if (h_low >= 0 && w_high <= width - 1) + { + const int ptr2 = h_low_ptr_offset + w_high_ptr_offset + base_ptr; + v2 = bottom_data[ptr2]; + grad_h_weight -= lw * v2; + grad_w_weight += hh * v2; + atomicAdd(grad_value+ptr2, w2*top_grad_value); + } + scalar_t v3 = 0; + if (h_high <= height - 1 && w_low >= 0) + { + const int ptr3 = h_high_ptr_offset + w_low_ptr_offset + base_ptr; + v3 = bottom_data[ptr3]; + grad_h_weight += hw * v3; + grad_w_weight -= lh * v3; + atomicAdd(grad_value+ptr3, w3*top_grad_value); + } + scalar_t v4 = 0; + if (h_high <= height - 1 && w_high <= width - 1) + { + const int ptr4 = h_high_ptr_offset + w_high_ptr_offset + base_ptr; + v4 = bottom_data[ptr4]; + grad_h_weight += lw * v4; + grad_w_weight += lh * v4; + atomicAdd(grad_value+ptr4, w4*top_grad_value); + } + + const scalar_t val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); + atomicAdd(grad_attn_weight, top_grad * val); + atomicAdd(grad_sampling_loc, width * grad_w_weight * top_grad_value); + atomicAdd(grad_sampling_loc + 1, height * grad_h_weight * top_grad_value); +} + + +template +__global__ void ms_deformable_im2col_gpu_kernel(const int n, + const scalar_t *data_value, + const int64_t *data_spatial_shapes, + const int64_t *data_level_start_index, + const scalar_t *data_sampling_loc, + const scalar_t *data_attn_weight, + const int batch_size, + const int spatial_size, + const int num_heads, + const int channels, + const int num_levels, + const int num_query, + const int num_point, + scalar_t *data_col) +{ + CUDA_KERNEL_LOOP(index, n) + { + int _temp = index; + const int c_col = _temp % channels; + _temp /= channels; + const int sampling_index = _temp; + const int m_col = _temp % num_heads; + _temp /= num_heads; + const int q_col = _temp % num_query; + _temp /= num_query; + const int b_col = _temp; + + scalar_t *data_col_ptr = data_col + index; + int data_weight_ptr = sampling_index * num_levels * num_point; + int data_loc_w_ptr = data_weight_ptr << 1; + const int qid_stride = num_heads * channels; + const int data_value_ptr_init_offset = b_col * spatial_size * qid_stride; + scalar_t col = 0; + + for (int l_col=0; l_col < num_levels; ++l_col) + { + const int level_start_id = data_level_start_index[l_col]; + const int spatial_h_ptr = l_col << 1; + const int spatial_h = data_spatial_shapes[spatial_h_ptr]; + const int spatial_w = data_spatial_shapes[spatial_h_ptr + 1]; + const scalar_t *data_value_ptr = data_value + (data_value_ptr_init_offset + level_start_id * qid_stride); + for (int p_col=0; p_col < num_point; ++p_col) + { + const scalar_t loc_w = data_sampling_loc[data_loc_w_ptr]; + const scalar_t loc_h = data_sampling_loc[data_loc_w_ptr + 1]; + const scalar_t weight = data_attn_weight[data_weight_ptr]; + + const scalar_t h_im = loc_h * spatial_h - 0.5; + const scalar_t w_im = loc_w * spatial_w - 0.5; + + if (h_im > -1 && w_im > -1 && h_im < spatial_h && w_im < spatial_w) + { + col += ms_deform_attn_im2col_bilinear(data_value_ptr, spatial_h, spatial_w, num_heads, channels, h_im, w_im, m_col, c_col) * weight; + } + + data_weight_ptr += 1; + data_loc_w_ptr += 2; + } + } + *data_col_ptr = col; + } +} + +template +__global__ void ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v1(const int n, + const scalar_t *grad_col, + const scalar_t *data_value, + const int64_t *data_spatial_shapes, + const int64_t *data_level_start_index, + const scalar_t *data_sampling_loc, + const scalar_t *data_attn_weight, + const int batch_size, + const int spatial_size, + const int num_heads, + const int channels, + const int num_levels, + const int num_query, + const int num_point, + scalar_t *grad_value, + scalar_t *grad_sampling_loc, + scalar_t *grad_attn_weight) +{ + CUDA_KERNEL_LOOP(index, n) + { + __shared__ scalar_t cache_grad_sampling_loc[blockSize * 2]; + __shared__ scalar_t cache_grad_attn_weight[blockSize]; + unsigned int tid = threadIdx.x; + int _temp = index; + const int c_col = _temp % channels; + _temp /= channels; + const int sampling_index = _temp; + const int m_col = _temp % num_heads; + _temp /= num_heads; + const int q_col = _temp % num_query; + _temp /= num_query; + const int b_col = _temp; + + const scalar_t top_grad = grad_col[index]; + + int data_weight_ptr = sampling_index * num_levels * num_point; + int data_loc_w_ptr = data_weight_ptr << 1; + const int grad_sampling_ptr = data_weight_ptr; + grad_sampling_loc += grad_sampling_ptr << 1; + grad_attn_weight += grad_sampling_ptr; + const int grad_weight_stride = 1; + const int grad_loc_stride = 2; + const int qid_stride = num_heads * channels; + const int data_value_ptr_init_offset = b_col * spatial_size * qid_stride; + + for (int l_col=0; l_col < num_levels; ++l_col) + { + const int level_start_id = data_level_start_index[l_col]; + const int spatial_h_ptr = l_col << 1; + const int spatial_h = data_spatial_shapes[spatial_h_ptr]; + const int spatial_w = data_spatial_shapes[spatial_h_ptr + 1]; + const int value_ptr_offset = data_value_ptr_init_offset + level_start_id * qid_stride; + const scalar_t *data_value_ptr = data_value + value_ptr_offset; + scalar_t *grad_value_ptr = grad_value + value_ptr_offset; + + for (int p_col=0; p_col < num_point; ++p_col) + { + const scalar_t loc_w = data_sampling_loc[data_loc_w_ptr]; + const scalar_t loc_h = data_sampling_loc[data_loc_w_ptr + 1]; + const scalar_t weight = data_attn_weight[data_weight_ptr]; + + const scalar_t h_im = loc_h * spatial_h - 0.5; + const scalar_t w_im = loc_w * spatial_w - 0.5; + *(cache_grad_sampling_loc+(threadIdx.x << 1)) = 0; + *(cache_grad_sampling_loc+((threadIdx.x << 1) + 1)) = 0; + *(cache_grad_attn_weight+threadIdx.x)=0; + if (h_im > -1 && w_im > -1 && h_im < spatial_h && w_im < spatial_w) + { + ms_deform_attn_col2im_bilinear( + data_value_ptr, spatial_h, spatial_w, num_heads, channels, h_im, w_im, m_col, c_col, + top_grad, weight, grad_value_ptr, + cache_grad_sampling_loc+(threadIdx.x << 1), cache_grad_attn_weight+threadIdx.x); + } + + __syncthreads(); + if (tid == 0) + { + scalar_t _grad_w=cache_grad_sampling_loc[0], _grad_h=cache_grad_sampling_loc[1], _grad_a=cache_grad_attn_weight[0]; + int sid=2; + for (unsigned int tid = 1; tid < blockSize; ++tid) + { + _grad_w += cache_grad_sampling_loc[sid]; + _grad_h += cache_grad_sampling_loc[sid + 1]; + _grad_a += cache_grad_attn_weight[tid]; + sid += 2; + } + + + *grad_sampling_loc = _grad_w; + *(grad_sampling_loc + 1) = _grad_h; + *grad_attn_weight = _grad_a; + } + __syncthreads(); + + data_weight_ptr += 1; + data_loc_w_ptr += 2; + grad_attn_weight += grad_weight_stride; + grad_sampling_loc += grad_loc_stride; + } + } + } +} + + +template +__global__ void ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v2(const int n, + const scalar_t *grad_col, + const scalar_t *data_value, + const int64_t *data_spatial_shapes, + const int64_t *data_level_start_index, + const scalar_t *data_sampling_loc, + const scalar_t *data_attn_weight, + const int batch_size, + const int spatial_size, + const int num_heads, + const int channels, + const int num_levels, + const int num_query, + const int num_point, + scalar_t *grad_value, + scalar_t *grad_sampling_loc, + scalar_t *grad_attn_weight) +{ + CUDA_KERNEL_LOOP(index, n) + { + __shared__ scalar_t cache_grad_sampling_loc[blockSize * 2]; + __shared__ scalar_t cache_grad_attn_weight[blockSize]; + unsigned int tid = threadIdx.x; + int _temp = index; + const int c_col = _temp % channels; + _temp /= channels; + const int sampling_index = _temp; + const int m_col = _temp % num_heads; + _temp /= num_heads; + const int q_col = _temp % num_query; + _temp /= num_query; + const int b_col = _temp; + + const scalar_t top_grad = grad_col[index]; + + int data_weight_ptr = sampling_index * num_levels * num_point; + int data_loc_w_ptr = data_weight_ptr << 1; + const int grad_sampling_ptr = data_weight_ptr; + grad_sampling_loc += grad_sampling_ptr << 1; + grad_attn_weight += grad_sampling_ptr; + const int grad_weight_stride = 1; + const int grad_loc_stride = 2; + const int qid_stride = num_heads * channels; + const int data_value_ptr_init_offset = b_col * spatial_size * qid_stride; + + for (int l_col=0; l_col < num_levels; ++l_col) + { + const int level_start_id = data_level_start_index[l_col]; + const int spatial_h_ptr = l_col << 1; + const int spatial_h = data_spatial_shapes[spatial_h_ptr]; + const int spatial_w = data_spatial_shapes[spatial_h_ptr + 1]; + const int value_ptr_offset = data_value_ptr_init_offset + level_start_id * qid_stride; + const scalar_t *data_value_ptr = data_value + value_ptr_offset; + scalar_t *grad_value_ptr = grad_value + value_ptr_offset; + + for (int p_col=0; p_col < num_point; ++p_col) + { + const scalar_t loc_w = data_sampling_loc[data_loc_w_ptr]; + const scalar_t loc_h = data_sampling_loc[data_loc_w_ptr + 1]; + const scalar_t weight = data_attn_weight[data_weight_ptr]; + + const scalar_t h_im = loc_h * spatial_h - 0.5; + const scalar_t w_im = loc_w * spatial_w - 0.5; + *(cache_grad_sampling_loc+(threadIdx.x << 1)) = 0; + *(cache_grad_sampling_loc+((threadIdx.x << 1) + 1)) = 0; + *(cache_grad_attn_weight+threadIdx.x)=0; + if (h_im > -1 && w_im > -1 && h_im < spatial_h && w_im < spatial_w) + { + ms_deform_attn_col2im_bilinear( + data_value_ptr, spatial_h, spatial_w, num_heads, channels, h_im, w_im, m_col, c_col, + top_grad, weight, grad_value_ptr, + cache_grad_sampling_loc+(threadIdx.x << 1), cache_grad_attn_weight+threadIdx.x); + } + + __syncthreads(); + + for (unsigned int s=blockSize/2; s>0; s>>=1) + { + if (tid < s) { + const unsigned int xid1 = tid << 1; + const unsigned int xid2 = (tid + s) << 1; + cache_grad_attn_weight[tid] += cache_grad_attn_weight[tid + s]; + cache_grad_sampling_loc[xid1] += cache_grad_sampling_loc[xid2]; + cache_grad_sampling_loc[xid1 + 1] += cache_grad_sampling_loc[xid2 + 1]; + } + __syncthreads(); + } + + if (tid == 0) + { + *grad_sampling_loc = cache_grad_sampling_loc[0]; + *(grad_sampling_loc + 1) = cache_grad_sampling_loc[1]; + *grad_attn_weight = cache_grad_attn_weight[0]; + } + __syncthreads(); + + data_weight_ptr += 1; + data_loc_w_ptr += 2; + grad_attn_weight += grad_weight_stride; + grad_sampling_loc += grad_loc_stride; + } + } + } +} + + +template +__global__ void ms_deformable_col2im_gpu_kernel_shm_reduce_v1(const int n, + const scalar_t *grad_col, + const scalar_t *data_value, + const int64_t *data_spatial_shapes, + const int64_t *data_level_start_index, + const scalar_t *data_sampling_loc, + const scalar_t *data_attn_weight, + const int batch_size, + const int spatial_size, + const int num_heads, + const int channels, + const int num_levels, + const int num_query, + const int num_point, + scalar_t *grad_value, + scalar_t *grad_sampling_loc, + scalar_t *grad_attn_weight) +{ + CUDA_KERNEL_LOOP(index, n) + { + extern __shared__ int _s[]; + scalar_t* cache_grad_sampling_loc = (scalar_t*)_s; + scalar_t* cache_grad_attn_weight = cache_grad_sampling_loc + 2 * blockDim.x; + unsigned int tid = threadIdx.x; + int _temp = index; + const int c_col = _temp % channels; + _temp /= channels; + const int sampling_index = _temp; + const int m_col = _temp % num_heads; + _temp /= num_heads; + const int q_col = _temp % num_query; + _temp /= num_query; + const int b_col = _temp; + + const scalar_t top_grad = grad_col[index]; + + int data_weight_ptr = sampling_index * num_levels * num_point; + int data_loc_w_ptr = data_weight_ptr << 1; + const int grad_sampling_ptr = data_weight_ptr; + grad_sampling_loc += grad_sampling_ptr << 1; + grad_attn_weight += grad_sampling_ptr; + const int grad_weight_stride = 1; + const int grad_loc_stride = 2; + const int qid_stride = num_heads * channels; + const int data_value_ptr_init_offset = b_col * spatial_size * qid_stride; + + for (int l_col=0; l_col < num_levels; ++l_col) + { + const int level_start_id = data_level_start_index[l_col]; + const int spatial_h_ptr = l_col << 1; + const int spatial_h = data_spatial_shapes[spatial_h_ptr]; + const int spatial_w = data_spatial_shapes[spatial_h_ptr + 1]; + const int value_ptr_offset = data_value_ptr_init_offset + level_start_id * qid_stride; + const scalar_t *data_value_ptr = data_value + value_ptr_offset; + scalar_t *grad_value_ptr = grad_value + value_ptr_offset; + + for (int p_col=0; p_col < num_point; ++p_col) + { + const scalar_t loc_w = data_sampling_loc[data_loc_w_ptr]; + const scalar_t loc_h = data_sampling_loc[data_loc_w_ptr + 1]; + const scalar_t weight = data_attn_weight[data_weight_ptr]; + + const scalar_t h_im = loc_h * spatial_h - 0.5; + const scalar_t w_im = loc_w * spatial_w - 0.5; + *(cache_grad_sampling_loc+(threadIdx.x << 1)) = 0; + *(cache_grad_sampling_loc+((threadIdx.x << 1) + 1)) = 0; + *(cache_grad_attn_weight+threadIdx.x)=0; + if (h_im > -1 && w_im > -1 && h_im < spatial_h && w_im < spatial_w) + { + ms_deform_attn_col2im_bilinear( + data_value_ptr, spatial_h, spatial_w, num_heads, channels, h_im, w_im, m_col, c_col, + top_grad, weight, grad_value_ptr, + cache_grad_sampling_loc+(threadIdx.x << 1), cache_grad_attn_weight+threadIdx.x); + } + + __syncthreads(); + if (tid == 0) + { + scalar_t _grad_w=cache_grad_sampling_loc[0], _grad_h=cache_grad_sampling_loc[1], _grad_a=cache_grad_attn_weight[0]; + int sid=2; + for (unsigned int tid = 1; tid < blockDim.x; ++tid) + { + _grad_w += cache_grad_sampling_loc[sid]; + _grad_h += cache_grad_sampling_loc[sid + 1]; + _grad_a += cache_grad_attn_weight[tid]; + sid += 2; + } + + + *grad_sampling_loc = _grad_w; + *(grad_sampling_loc + 1) = _grad_h; + *grad_attn_weight = _grad_a; + } + __syncthreads(); + + data_weight_ptr += 1; + data_loc_w_ptr += 2; + grad_attn_weight += grad_weight_stride; + grad_sampling_loc += grad_loc_stride; + } + } + } +} + +template +__global__ void ms_deformable_col2im_gpu_kernel_shm_reduce_v2(const int n, + const scalar_t *grad_col, + const scalar_t *data_value, + const int64_t *data_spatial_shapes, + const int64_t *data_level_start_index, + const scalar_t *data_sampling_loc, + const scalar_t *data_attn_weight, + const int batch_size, + const int spatial_size, + const int num_heads, + const int channels, + const int num_levels, + const int num_query, + const int num_point, + scalar_t *grad_value, + scalar_t *grad_sampling_loc, + scalar_t *grad_attn_weight) +{ + CUDA_KERNEL_LOOP(index, n) + { + extern __shared__ int _s[]; + scalar_t* cache_grad_sampling_loc = (scalar_t*)_s; + scalar_t* cache_grad_attn_weight = cache_grad_sampling_loc + 2 * blockDim.x; + unsigned int tid = threadIdx.x; + int _temp = index; + const int c_col = _temp % channels; + _temp /= channels; + const int sampling_index = _temp; + const int m_col = _temp % num_heads; + _temp /= num_heads; + const int q_col = _temp % num_query; + _temp /= num_query; + const int b_col = _temp; + + const scalar_t top_grad = grad_col[index]; + + int data_weight_ptr = sampling_index * num_levels * num_point; + int data_loc_w_ptr = data_weight_ptr << 1; + const int grad_sampling_ptr = data_weight_ptr; + grad_sampling_loc += grad_sampling_ptr << 1; + grad_attn_weight += grad_sampling_ptr; + const int grad_weight_stride = 1; + const int grad_loc_stride = 2; + const int qid_stride = num_heads * channels; + const int data_value_ptr_init_offset = b_col * spatial_size * qid_stride; + + for (int l_col=0; l_col < num_levels; ++l_col) + { + const int level_start_id = data_level_start_index[l_col]; + const int spatial_h_ptr = l_col << 1; + const int spatial_h = data_spatial_shapes[spatial_h_ptr]; + const int spatial_w = data_spatial_shapes[spatial_h_ptr + 1]; + const int value_ptr_offset = data_value_ptr_init_offset + level_start_id * qid_stride; + const scalar_t *data_value_ptr = data_value + value_ptr_offset; + scalar_t *grad_value_ptr = grad_value + value_ptr_offset; + + for (int p_col=0; p_col < num_point; ++p_col) + { + const scalar_t loc_w = data_sampling_loc[data_loc_w_ptr]; + const scalar_t loc_h = data_sampling_loc[data_loc_w_ptr + 1]; + const scalar_t weight = data_attn_weight[data_weight_ptr]; + + const scalar_t h_im = loc_h * spatial_h - 0.5; + const scalar_t w_im = loc_w * spatial_w - 0.5; + *(cache_grad_sampling_loc+(threadIdx.x << 1)) = 0; + *(cache_grad_sampling_loc+((threadIdx.x << 1) + 1)) = 0; + *(cache_grad_attn_weight+threadIdx.x)=0; + if (h_im > -1 && w_im > -1 && h_im < spatial_h && w_im < spatial_w) + { + ms_deform_attn_col2im_bilinear( + data_value_ptr, spatial_h, spatial_w, num_heads, channels, h_im, w_im, m_col, c_col, + top_grad, weight, grad_value_ptr, + cache_grad_sampling_loc+(threadIdx.x << 1), cache_grad_attn_weight+threadIdx.x); + } + + __syncthreads(); + + for (unsigned int s=blockDim.x/2, spre=blockDim.x; s>0; s>>=1, spre>>=1) + { + if (tid < s) { + const unsigned int xid1 = tid << 1; + const unsigned int xid2 = (tid + s) << 1; + cache_grad_attn_weight[tid] += cache_grad_attn_weight[tid + s]; + cache_grad_sampling_loc[xid1] += cache_grad_sampling_loc[xid2]; + cache_grad_sampling_loc[xid1 + 1] += cache_grad_sampling_loc[xid2 + 1]; + if (tid + (s << 1) < spre) + { + cache_grad_attn_weight[tid] += cache_grad_attn_weight[tid + (s << 1)]; + cache_grad_sampling_loc[xid1] += cache_grad_sampling_loc[xid2 + (s << 1)]; + cache_grad_sampling_loc[xid1 + 1] += cache_grad_sampling_loc[xid2 + 1 + (s << 1)]; + } + } + __syncthreads(); + } + + if (tid == 0) + { + *grad_sampling_loc = cache_grad_sampling_loc[0]; + *(grad_sampling_loc + 1) = cache_grad_sampling_loc[1]; + *grad_attn_weight = cache_grad_attn_weight[0]; + } + __syncthreads(); + + data_weight_ptr += 1; + data_loc_w_ptr += 2; + grad_attn_weight += grad_weight_stride; + grad_sampling_loc += grad_loc_stride; + } + } + } +} + +template +__global__ void ms_deformable_col2im_gpu_kernel_shm_reduce_v2_multi_blocks(const int n, + const scalar_t *grad_col, + const scalar_t *data_value, + const int64_t *data_spatial_shapes, + const int64_t *data_level_start_index, + const scalar_t *data_sampling_loc, + const scalar_t *data_attn_weight, + const int batch_size, + const int spatial_size, + const int num_heads, + const int channels, + const int num_levels, + const int num_query, + const int num_point, + scalar_t *grad_value, + scalar_t *grad_sampling_loc, + scalar_t *grad_attn_weight) +{ + CUDA_KERNEL_LOOP(index, n) + { + extern __shared__ int _s[]; + scalar_t* cache_grad_sampling_loc = (scalar_t*)_s; + scalar_t* cache_grad_attn_weight = cache_grad_sampling_loc + 2 * blockDim.x; + unsigned int tid = threadIdx.x; + int _temp = index; + const int c_col = _temp % channels; + _temp /= channels; + const int sampling_index = _temp; + const int m_col = _temp % num_heads; + _temp /= num_heads; + const int q_col = _temp % num_query; + _temp /= num_query; + const int b_col = _temp; + + const scalar_t top_grad = grad_col[index]; + + int data_weight_ptr = sampling_index * num_levels * num_point; + int data_loc_w_ptr = data_weight_ptr << 1; + const int grad_sampling_ptr = data_weight_ptr; + grad_sampling_loc += grad_sampling_ptr << 1; + grad_attn_weight += grad_sampling_ptr; + const int grad_weight_stride = 1; + const int grad_loc_stride = 2; + const int qid_stride = num_heads * channels; + const int data_value_ptr_init_offset = b_col * spatial_size * qid_stride; + + for (int l_col=0; l_col < num_levels; ++l_col) + { + const int level_start_id = data_level_start_index[l_col]; + const int spatial_h_ptr = l_col << 1; + const int spatial_h = data_spatial_shapes[spatial_h_ptr]; + const int spatial_w = data_spatial_shapes[spatial_h_ptr + 1]; + const int value_ptr_offset = data_value_ptr_init_offset + level_start_id * qid_stride; + const scalar_t *data_value_ptr = data_value + value_ptr_offset; + scalar_t *grad_value_ptr = grad_value + value_ptr_offset; + + for (int p_col=0; p_col < num_point; ++p_col) + { + const scalar_t loc_w = data_sampling_loc[data_loc_w_ptr]; + const scalar_t loc_h = data_sampling_loc[data_loc_w_ptr + 1]; + const scalar_t weight = data_attn_weight[data_weight_ptr]; + + const scalar_t h_im = loc_h * spatial_h - 0.5; + const scalar_t w_im = loc_w * spatial_w - 0.5; + *(cache_grad_sampling_loc+(threadIdx.x << 1)) = 0; + *(cache_grad_sampling_loc+((threadIdx.x << 1) + 1)) = 0; + *(cache_grad_attn_weight+threadIdx.x)=0; + if (h_im > -1 && w_im > -1 && h_im < spatial_h && w_im < spatial_w) + { + ms_deform_attn_col2im_bilinear( + data_value_ptr, spatial_h, spatial_w, num_heads, channels, h_im, w_im, m_col, c_col, + top_grad, weight, grad_value_ptr, + cache_grad_sampling_loc+(threadIdx.x << 1), cache_grad_attn_weight+threadIdx.x); + } + + __syncthreads(); + + for (unsigned int s=blockDim.x/2, spre=blockDim.x; s>0; s>>=1, spre>>=1) + { + if (tid < s) { + const unsigned int xid1 = tid << 1; + const unsigned int xid2 = (tid + s) << 1; + cache_grad_attn_weight[tid] += cache_grad_attn_weight[tid + s]; + cache_grad_sampling_loc[xid1] += cache_grad_sampling_loc[xid2]; + cache_grad_sampling_loc[xid1 + 1] += cache_grad_sampling_loc[xid2 + 1]; + if (tid + (s << 1) < spre) + { + cache_grad_attn_weight[tid] += cache_grad_attn_weight[tid + (s << 1)]; + cache_grad_sampling_loc[xid1] += cache_grad_sampling_loc[xid2 + (s << 1)]; + cache_grad_sampling_loc[xid1 + 1] += cache_grad_sampling_loc[xid2 + 1 + (s << 1)]; + } + } + __syncthreads(); + } + + if (tid == 0) + { + atomicAdd(grad_sampling_loc, cache_grad_sampling_loc[0]); + atomicAdd(grad_sampling_loc + 1, cache_grad_sampling_loc[1]); + atomicAdd(grad_attn_weight, cache_grad_attn_weight[0]); + } + __syncthreads(); + + data_weight_ptr += 1; + data_loc_w_ptr += 2; + grad_attn_weight += grad_weight_stride; + grad_sampling_loc += grad_loc_stride; + } + } + } +} + + +template +__global__ void ms_deformable_col2im_gpu_kernel_gm(const int n, + const scalar_t *grad_col, + const scalar_t *data_value, + const int64_t *data_spatial_shapes, + const int64_t *data_level_start_index, + const scalar_t *data_sampling_loc, + const scalar_t *data_attn_weight, + const int batch_size, + const int spatial_size, + const int num_heads, + const int channels, + const int num_levels, + const int num_query, + const int num_point, + scalar_t *grad_value, + scalar_t *grad_sampling_loc, + scalar_t *grad_attn_weight) +{ + CUDA_KERNEL_LOOP(index, n) + { + int _temp = index; + const int c_col = _temp % channels; + _temp /= channels; + const int sampling_index = _temp; + const int m_col = _temp % num_heads; + _temp /= num_heads; + const int q_col = _temp % num_query; + _temp /= num_query; + const int b_col = _temp; + + const scalar_t top_grad = grad_col[index]; + + int data_weight_ptr = sampling_index * num_levels * num_point; + int data_loc_w_ptr = data_weight_ptr << 1; + const int grad_sampling_ptr = data_weight_ptr; + grad_sampling_loc += grad_sampling_ptr << 1; + grad_attn_weight += grad_sampling_ptr; + const int grad_weight_stride = 1; + const int grad_loc_stride = 2; + const int qid_stride = num_heads * channels; + const int data_value_ptr_init_offset = b_col * spatial_size * qid_stride; + + for (int l_col=0; l_col < num_levels; ++l_col) + { + const int level_start_id = data_level_start_index[l_col]; + const int spatial_h_ptr = l_col << 1; + const int spatial_h = data_spatial_shapes[spatial_h_ptr]; + const int spatial_w = data_spatial_shapes[spatial_h_ptr + 1]; + const int value_ptr_offset = data_value_ptr_init_offset + level_start_id * qid_stride; + const scalar_t *data_value_ptr = data_value + value_ptr_offset; + scalar_t *grad_value_ptr = grad_value + value_ptr_offset; + + for (int p_col=0; p_col < num_point; ++p_col) + { + const scalar_t loc_w = data_sampling_loc[data_loc_w_ptr]; + const scalar_t loc_h = data_sampling_loc[data_loc_w_ptr + 1]; + const scalar_t weight = data_attn_weight[data_weight_ptr]; + + const scalar_t h_im = loc_h * spatial_h - 0.5; + const scalar_t w_im = loc_w * spatial_w - 0.5; + if (h_im > -1 && w_im > -1 && h_im < spatial_h && w_im < spatial_w) + { + ms_deform_attn_col2im_bilinear_gm( + data_value_ptr, spatial_h, spatial_w, num_heads, channels, h_im, w_im, m_col, c_col, + top_grad, weight, grad_value_ptr, + grad_sampling_loc, grad_attn_weight); + } + data_weight_ptr += 1; + data_loc_w_ptr += 2; + grad_attn_weight += grad_weight_stride; + grad_sampling_loc += grad_loc_stride; + } + } + } +} + + +template +void ms_deformable_im2col_cuda(cudaStream_t stream, + const scalar_t* data_value, + const int64_t* data_spatial_shapes, + const int64_t* data_level_start_index, + const scalar_t* data_sampling_loc, + const scalar_t* data_attn_weight, + const int batch_size, + const int spatial_size, + const int num_heads, + const int channels, + const int num_levels, + const int num_query, + const int num_point, + scalar_t* data_col) +{ + const int num_kernels = batch_size * num_query * num_heads * channels; + const int num_actual_kernels = batch_size * num_query * num_heads * channels; + const int num_threads = CUDA_NUM_THREADS; + ms_deformable_im2col_gpu_kernel + <<>>( + num_kernels, data_value, data_spatial_shapes, data_level_start_index, data_sampling_loc, data_attn_weight, + batch_size, spatial_size, num_heads, channels, num_levels, num_query, num_point, data_col); + + cudaError_t err = cudaGetLastError(); + if (err != cudaSuccess) + { + printf("error in ms_deformable_im2col_cuda: %s\n", cudaGetErrorString(err)); + } + +} + +template +void ms_deformable_col2im_cuda(cudaStream_t stream, + const scalar_t* grad_col, + const scalar_t* data_value, + const int64_t * data_spatial_shapes, + const int64_t * data_level_start_index, + const scalar_t * data_sampling_loc, + const scalar_t * data_attn_weight, + const int batch_size, + const int spatial_size, + const int num_heads, + const int channels, + const int num_levels, + const int num_query, + const int num_point, + scalar_t* grad_value, + scalar_t* grad_sampling_loc, + scalar_t* grad_attn_weight) +{ + const int num_threads = (channels > CUDA_NUM_THREADS)?CUDA_NUM_THREADS:channels; + const int num_kernels = batch_size * num_query * num_heads * channels; + const int num_actual_kernels = batch_size * num_query * num_heads * channels; + if (channels > 1024) + { + if ((channels & 1023) == 0) + { + ms_deformable_col2im_gpu_kernel_shm_reduce_v2_multi_blocks + <<>>( + num_kernels, + grad_col, + data_value, + data_spatial_shapes, + data_level_start_index, + data_sampling_loc, + data_attn_weight, + batch_size, + spatial_size, + num_heads, + channels, + num_levels, + num_query, + num_point, + grad_value, + grad_sampling_loc, + grad_attn_weight); + } + else + { + ms_deformable_col2im_gpu_kernel_gm + <<>>( + num_kernels, + grad_col, + data_value, + data_spatial_shapes, + data_level_start_index, + data_sampling_loc, + data_attn_weight, + batch_size, + spatial_size, + num_heads, + channels, + num_levels, + num_query, + num_point, + grad_value, + grad_sampling_loc, + grad_attn_weight); + } + } + else{ + switch(channels) + { + case 1: + ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v1 + <<>>( + num_kernels, + grad_col, + data_value, + data_spatial_shapes, + data_level_start_index, + data_sampling_loc, + data_attn_weight, + batch_size, + spatial_size, + num_heads, + channels, + num_levels, + num_query, + num_point, + grad_value, + grad_sampling_loc, + grad_attn_weight); + break; + case 2: + ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v1 + <<>>( + num_kernels, + grad_col, + data_value, + data_spatial_shapes, + data_level_start_index, + data_sampling_loc, + data_attn_weight, + batch_size, + spatial_size, + num_heads, + channels, + num_levels, + num_query, + num_point, + grad_value, + grad_sampling_loc, + grad_attn_weight); + break; + case 4: + ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v1 + <<>>( + num_kernels, + grad_col, + data_value, + data_spatial_shapes, + data_level_start_index, + data_sampling_loc, + data_attn_weight, + batch_size, + spatial_size, + num_heads, + channels, + num_levels, + num_query, + num_point, + grad_value, + grad_sampling_loc, + grad_attn_weight); + break; + case 8: + ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v1 + <<>>( + num_kernels, + grad_col, + data_value, + data_spatial_shapes, + data_level_start_index, + data_sampling_loc, + data_attn_weight, + batch_size, + spatial_size, + num_heads, + channels, + num_levels, + num_query, + num_point, + grad_value, + grad_sampling_loc, + grad_attn_weight); + break; + case 16: + ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v1 + <<>>( + num_kernels, + grad_col, + data_value, + data_spatial_shapes, + data_level_start_index, + data_sampling_loc, + data_attn_weight, + batch_size, + spatial_size, + num_heads, + channels, + num_levels, + num_query, + num_point, + grad_value, + grad_sampling_loc, + grad_attn_weight); + break; + case 32: + ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v1 + <<>>( + num_kernels, + grad_col, + data_value, + data_spatial_shapes, + data_level_start_index, + data_sampling_loc, + data_attn_weight, + batch_size, + spatial_size, + num_heads, + channels, + num_levels, + num_query, + num_point, + grad_value, + grad_sampling_loc, + grad_attn_weight); + break; + case 64: + ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v2 + <<>>( + num_kernels, + grad_col, + data_value, + data_spatial_shapes, + data_level_start_index, + data_sampling_loc, + data_attn_weight, + batch_size, + spatial_size, + num_heads, + channels, + num_levels, + num_query, + num_point, + grad_value, + grad_sampling_loc, + grad_attn_weight); + break; + case 128: + ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v2 + <<>>( + num_kernels, + grad_col, + data_value, + data_spatial_shapes, + data_level_start_index, + data_sampling_loc, + data_attn_weight, + batch_size, + spatial_size, + num_heads, + channels, + num_levels, + num_query, + num_point, + grad_value, + grad_sampling_loc, + grad_attn_weight); + break; + case 256: + ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v2 + <<>>( + num_kernels, + grad_col, + data_value, + data_spatial_shapes, + data_level_start_index, + data_sampling_loc, + data_attn_weight, + batch_size, + spatial_size, + num_heads, + channels, + num_levels, + num_query, + num_point, + grad_value, + grad_sampling_loc, + grad_attn_weight); + break; + case 512: + ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v2 + <<>>( + num_kernels, + grad_col, + data_value, + data_spatial_shapes, + data_level_start_index, + data_sampling_loc, + data_attn_weight, + batch_size, + spatial_size, + num_heads, + channels, + num_levels, + num_query, + num_point, + grad_value, + grad_sampling_loc, + grad_attn_weight); + break; + case 1024: + ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v2 + <<>>( + num_kernels, + grad_col, + data_value, + data_spatial_shapes, + data_level_start_index, + data_sampling_loc, + data_attn_weight, + batch_size, + spatial_size, + num_heads, + channels, + num_levels, + num_query, + num_point, + grad_value, + grad_sampling_loc, + grad_attn_weight); + break; + default: + if (channels < 64) + { + ms_deformable_col2im_gpu_kernel_shm_reduce_v1 + <<>>( + num_kernels, + grad_col, + data_value, + data_spatial_shapes, + data_level_start_index, + data_sampling_loc, + data_attn_weight, + batch_size, + spatial_size, + num_heads, + channels, + num_levels, + num_query, + num_point, + grad_value, + grad_sampling_loc, + grad_attn_weight); + } + else + { + ms_deformable_col2im_gpu_kernel_shm_reduce_v2 + <<>>( + num_kernels, + grad_col, + data_value, + data_spatial_shapes, + data_level_start_index, + data_sampling_loc, + data_attn_weight, + batch_size, + spatial_size, + num_heads, + channels, + num_levels, + num_query, + num_point, + grad_value, + grad_sampling_loc, + grad_attn_weight); + } + } + } + cudaError_t err = cudaGetLastError(); + if (err != cudaSuccess) + { + printf("error in ms_deformable_col2im_cuda: %s\n", cudaGetErrorString(err)); + } + +} diff --git a/venv/lib/python3.13/site-packages/transformers/kernels/deta/cuda/ms_deform_attn_cuda.h b/venv/lib/python3.13/site-packages/transformers/kernels/deta/cuda/ms_deform_attn_cuda.h new file mode 100644 index 0000000000000000000000000000000000000000..fbcf4543e66bb1162f42ce2ae57e1bac92243cb4 --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/kernels/deta/cuda/ms_deform_attn_cuda.h @@ -0,0 +1,29 @@ +/*! +************************************************************************************************** +* Deformable DETR +* Copyright (c) 2020 SenseTime. All Rights Reserved. +* Licensed under the Apache License, Version 2.0 [see LICENSE for details] +************************************************************************************************** +* Modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0 +************************************************************************************************** +*/ + +#pragma once +#include + +at::Tensor ms_deform_attn_cuda_forward( + const at::Tensor &value, + const at::Tensor &spatial_shapes, + const at::Tensor &level_start_index, + const at::Tensor &sampling_loc, + const at::Tensor &attn_weight, + const int im2col_step); + +std::vector ms_deform_attn_cuda_backward( + const at::Tensor &value, + const at::Tensor &spatial_shapes, + const at::Tensor &level_start_index, + const at::Tensor &sampling_loc, + const at::Tensor &attn_weight, + const at::Tensor &grad_output, + const int im2col_step); diff --git a/venv/lib/python3.13/site-packages/transformers/kernels/deta/cuda/ms_deform_im2col_cuda.cuh b/venv/lib/python3.13/site-packages/transformers/kernels/deta/cuda/ms_deform_im2col_cuda.cuh new file mode 100644 index 0000000000000000000000000000000000000000..c0db0c88c9db2c09d7f601937ea0f6ac480913bf --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/kernels/deta/cuda/ms_deform_im2col_cuda.cuh @@ -0,0 +1,1327 @@ +/*! +************************************************************************** +* Deformable DETR +* Copyright (c) 2020 SenseTime. All Rights Reserved. +* Licensed under the Apache License, Version 2.0 [see LICENSE for details] +************************************************************************** +* Modified from DCN (https://github.com/msracver/Deformable-ConvNets) +* Copyright (c) 2018 Microsoft +************************************************************************** +*/ + +#include +#include +#include + +#include +#include + +#include + +#define CUDA_KERNEL_LOOP(i, n) \ + for (int i = blockIdx.x * blockDim.x + threadIdx.x; \ + i < (n); \ + i += blockDim.x * gridDim.x) + +const int CUDA_NUM_THREADS = 1024; +inline int GET_BLOCKS(const int N, const int num_threads) +{ + return (N + num_threads - 1) / num_threads; +} + + +template +__device__ scalar_t ms_deform_attn_im2col_bilinear(const scalar_t* &bottom_data, + const int &height, const int &width, const int &nheads, const int &channels, + const scalar_t &h, const scalar_t &w, const int &m, const int &c) +{ + const int h_low = floor(h); + const int w_low = floor(w); + const int h_high = h_low + 1; + const int w_high = w_low + 1; + + const scalar_t lh = h - h_low; + const scalar_t lw = w - w_low; + const scalar_t hh = 1 - lh, hw = 1 - lw; + + const int w_stride = nheads * channels; + const int h_stride = width * w_stride; + const int h_low_ptr_offset = h_low * h_stride; + const int h_high_ptr_offset = h_low_ptr_offset + h_stride; + const int w_low_ptr_offset = w_low * w_stride; + const int w_high_ptr_offset = w_low_ptr_offset + w_stride; + const int base_ptr = m * channels + c; + + scalar_t v1 = 0; + if (h_low >= 0 && w_low >= 0) + { + const int ptr1 = h_low_ptr_offset + w_low_ptr_offset + base_ptr; + v1 = bottom_data[ptr1]; + } + scalar_t v2 = 0; + if (h_low >= 0 && w_high <= width - 1) + { + const int ptr2 = h_low_ptr_offset + w_high_ptr_offset + base_ptr; + v2 = bottom_data[ptr2]; + } + scalar_t v3 = 0; + if (h_high <= height - 1 && w_low >= 0) + { + const int ptr3 = h_high_ptr_offset + w_low_ptr_offset + base_ptr; + v3 = bottom_data[ptr3]; + } + scalar_t v4 = 0; + if (h_high <= height - 1 && w_high <= width - 1) + { + const int ptr4 = h_high_ptr_offset + w_high_ptr_offset + base_ptr; + v4 = bottom_data[ptr4]; + } + + const scalar_t w1 = hh * hw, w2 = hh * lw, w3 = lh * hw, w4 = lh * lw; + + const scalar_t val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); + return val; +} + + +template +__device__ void ms_deform_attn_col2im_bilinear(const scalar_t* &bottom_data, + const int &height, const int &width, const int &nheads, const int &channels, + const scalar_t &h, const scalar_t &w, const int &m, const int &c, + const scalar_t &top_grad, + const scalar_t &attn_weight, + scalar_t* &grad_value, + scalar_t* grad_sampling_loc, + scalar_t* grad_attn_weight) +{ + const int h_low = floor(h); + const int w_low = floor(w); + const int h_high = h_low + 1; + const int w_high = w_low + 1; + + const scalar_t lh = h - h_low; + const scalar_t lw = w - w_low; + const scalar_t hh = 1 - lh, hw = 1 - lw; + + const int w_stride = nheads * channels; + const int h_stride = width * w_stride; + const int h_low_ptr_offset = h_low * h_stride; + const int h_high_ptr_offset = h_low_ptr_offset + h_stride; + const int w_low_ptr_offset = w_low * w_stride; + const int w_high_ptr_offset = w_low_ptr_offset + w_stride; + const int base_ptr = m * channels + c; + + const scalar_t w1 = hh * hw, w2 = hh * lw, w3 = lh * hw, w4 = lh * lw; + const scalar_t top_grad_value = top_grad * attn_weight; + scalar_t grad_h_weight = 0, grad_w_weight = 0; + + scalar_t v1 = 0; + if (h_low >= 0 && w_low >= 0) + { + const int ptr1 = h_low_ptr_offset + w_low_ptr_offset + base_ptr; + v1 = bottom_data[ptr1]; + grad_h_weight -= hw * v1; + grad_w_weight -= hh * v1; + atomicAdd(grad_value+ptr1, w1*top_grad_value); + } + scalar_t v2 = 0; + if (h_low >= 0 && w_high <= width - 1) + { + const int ptr2 = h_low_ptr_offset + w_high_ptr_offset + base_ptr; + v2 = bottom_data[ptr2]; + grad_h_weight -= lw * v2; + grad_w_weight += hh * v2; + atomicAdd(grad_value+ptr2, w2*top_grad_value); + } + scalar_t v3 = 0; + if (h_high <= height - 1 && w_low >= 0) + { + const int ptr3 = h_high_ptr_offset + w_low_ptr_offset + base_ptr; + v3 = bottom_data[ptr3]; + grad_h_weight += hw * v3; + grad_w_weight -= lh * v3; + atomicAdd(grad_value+ptr3, w3*top_grad_value); + } + scalar_t v4 = 0; + if (h_high <= height - 1 && w_high <= width - 1) + { + const int ptr4 = h_high_ptr_offset + w_high_ptr_offset + base_ptr; + v4 = bottom_data[ptr4]; + grad_h_weight += lw * v4; + grad_w_weight += lh * v4; + atomicAdd(grad_value+ptr4, w4*top_grad_value); + } + + const scalar_t val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); + *grad_attn_weight = top_grad * val; + *grad_sampling_loc = width * grad_w_weight * top_grad_value; + *(grad_sampling_loc + 1) = height * grad_h_weight * top_grad_value; +} + + +template +__device__ void ms_deform_attn_col2im_bilinear_gm(const scalar_t* &bottom_data, + const int &height, const int &width, const int &nheads, const int &channels, + const scalar_t &h, const scalar_t &w, const int &m, const int &c, + const scalar_t &top_grad, + const scalar_t &attn_weight, + scalar_t* &grad_value, + scalar_t* grad_sampling_loc, + scalar_t* grad_attn_weight) +{ + const int h_low = floor(h); + const int w_low = floor(w); + const int h_high = h_low + 1; + const int w_high = w_low + 1; + + const scalar_t lh = h - h_low; + const scalar_t lw = w - w_low; + const scalar_t hh = 1 - lh, hw = 1 - lw; + + const int w_stride = nheads * channels; + const int h_stride = width * w_stride; + const int h_low_ptr_offset = h_low * h_stride; + const int h_high_ptr_offset = h_low_ptr_offset + h_stride; + const int w_low_ptr_offset = w_low * w_stride; + const int w_high_ptr_offset = w_low_ptr_offset + w_stride; + const int base_ptr = m * channels + c; + + const scalar_t w1 = hh * hw, w2 = hh * lw, w3 = lh * hw, w4 = lh * lw; + const scalar_t top_grad_value = top_grad * attn_weight; + scalar_t grad_h_weight = 0, grad_w_weight = 0; + + scalar_t v1 = 0; + if (h_low >= 0 && w_low >= 0) + { + const int ptr1 = h_low_ptr_offset + w_low_ptr_offset + base_ptr; + v1 = bottom_data[ptr1]; + grad_h_weight -= hw * v1; + grad_w_weight -= hh * v1; + atomicAdd(grad_value+ptr1, w1*top_grad_value); + } + scalar_t v2 = 0; + if (h_low >= 0 && w_high <= width - 1) + { + const int ptr2 = h_low_ptr_offset + w_high_ptr_offset + base_ptr; + v2 = bottom_data[ptr2]; + grad_h_weight -= lw * v2; + grad_w_weight += hh * v2; + atomicAdd(grad_value+ptr2, w2*top_grad_value); + } + scalar_t v3 = 0; + if (h_high <= height - 1 && w_low >= 0) + { + const int ptr3 = h_high_ptr_offset + w_low_ptr_offset + base_ptr; + v3 = bottom_data[ptr3]; + grad_h_weight += hw * v3; + grad_w_weight -= lh * v3; + atomicAdd(grad_value+ptr3, w3*top_grad_value); + } + scalar_t v4 = 0; + if (h_high <= height - 1 && w_high <= width - 1) + { + const int ptr4 = h_high_ptr_offset + w_high_ptr_offset + base_ptr; + v4 = bottom_data[ptr4]; + grad_h_weight += lw * v4; + grad_w_weight += lh * v4; + atomicAdd(grad_value+ptr4, w4*top_grad_value); + } + + const scalar_t val = (w1 * v1 + w2 * v2 + w3 * v3 + w4 * v4); + atomicAdd(grad_attn_weight, top_grad * val); + atomicAdd(grad_sampling_loc, width * grad_w_weight * top_grad_value); + atomicAdd(grad_sampling_loc + 1, height * grad_h_weight * top_grad_value); +} + + +template +__global__ void ms_deformable_im2col_gpu_kernel(const int n, + const scalar_t *data_value, + const int64_t *data_spatial_shapes, + const int64_t *data_level_start_index, + const scalar_t *data_sampling_loc, + const scalar_t *data_attn_weight, + const int batch_size, + const int spatial_size, + const int num_heads, + const int channels, + const int num_levels, + const int num_query, + const int num_point, + scalar_t *data_col) +{ + CUDA_KERNEL_LOOP(index, n) + { + int _temp = index; + const int c_col = _temp % channels; + _temp /= channels; + const int sampling_index = _temp; + const int m_col = _temp % num_heads; + _temp /= num_heads; + const int q_col = _temp % num_query; + _temp /= num_query; + const int b_col = _temp; + + scalar_t *data_col_ptr = data_col + index; + int data_weight_ptr = sampling_index * num_levels * num_point; + int data_loc_w_ptr = data_weight_ptr << 1; + const int qid_stride = num_heads * channels; + const int data_value_ptr_init_offset = b_col * spatial_size * qid_stride; + scalar_t col = 0; + + for (int l_col=0; l_col < num_levels; ++l_col) + { + const int level_start_id = data_level_start_index[l_col]; + const int spatial_h_ptr = l_col << 1; + const int spatial_h = data_spatial_shapes[spatial_h_ptr]; + const int spatial_w = data_spatial_shapes[spatial_h_ptr + 1]; + const scalar_t *data_value_ptr = data_value + (data_value_ptr_init_offset + level_start_id * qid_stride); + for (int p_col=0; p_col < num_point; ++p_col) + { + const scalar_t loc_w = data_sampling_loc[data_loc_w_ptr]; + const scalar_t loc_h = data_sampling_loc[data_loc_w_ptr + 1]; + const scalar_t weight = data_attn_weight[data_weight_ptr]; + + const scalar_t h_im = loc_h * spatial_h - 0.5; + const scalar_t w_im = loc_w * spatial_w - 0.5; + + if (h_im > -1 && w_im > -1 && h_im < spatial_h && w_im < spatial_w) + { + col += ms_deform_attn_im2col_bilinear(data_value_ptr, spatial_h, spatial_w, num_heads, channels, h_im, w_im, m_col, c_col) * weight; + } + + data_weight_ptr += 1; + data_loc_w_ptr += 2; + } + } + *data_col_ptr = col; + } +} + +template +__global__ void ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v1(const int n, + const scalar_t *grad_col, + const scalar_t *data_value, + const int64_t *data_spatial_shapes, + const int64_t *data_level_start_index, + const scalar_t *data_sampling_loc, + const scalar_t *data_attn_weight, + const int batch_size, + const int spatial_size, + const int num_heads, + const int channels, + const int num_levels, + const int num_query, + const int num_point, + scalar_t *grad_value, + scalar_t *grad_sampling_loc, + scalar_t *grad_attn_weight) +{ + CUDA_KERNEL_LOOP(index, n) + { + __shared__ scalar_t cache_grad_sampling_loc[blockSize * 2]; + __shared__ scalar_t cache_grad_attn_weight[blockSize]; + unsigned int tid = threadIdx.x; + int _temp = index; + const int c_col = _temp % channels; + _temp /= channels; + const int sampling_index = _temp; + const int m_col = _temp % num_heads; + _temp /= num_heads; + const int q_col = _temp % num_query; + _temp /= num_query; + const int b_col = _temp; + + const scalar_t top_grad = grad_col[index]; + + int data_weight_ptr = sampling_index * num_levels * num_point; + int data_loc_w_ptr = data_weight_ptr << 1; + const int grad_sampling_ptr = data_weight_ptr; + grad_sampling_loc += grad_sampling_ptr << 1; + grad_attn_weight += grad_sampling_ptr; + const int grad_weight_stride = 1; + const int grad_loc_stride = 2; + const int qid_stride = num_heads * channels; + const int data_value_ptr_init_offset = b_col * spatial_size * qid_stride; + + for (int l_col=0; l_col < num_levels; ++l_col) + { + const int level_start_id = data_level_start_index[l_col]; + const int spatial_h_ptr = l_col << 1; + const int spatial_h = data_spatial_shapes[spatial_h_ptr]; + const int spatial_w = data_spatial_shapes[spatial_h_ptr + 1]; + const int value_ptr_offset = data_value_ptr_init_offset + level_start_id * qid_stride; + const scalar_t *data_value_ptr = data_value + value_ptr_offset; + scalar_t *grad_value_ptr = grad_value + value_ptr_offset; + + for (int p_col=0; p_col < num_point; ++p_col) + { + const scalar_t loc_w = data_sampling_loc[data_loc_w_ptr]; + const scalar_t loc_h = data_sampling_loc[data_loc_w_ptr + 1]; + const scalar_t weight = data_attn_weight[data_weight_ptr]; + + const scalar_t h_im = loc_h * spatial_h - 0.5; + const scalar_t w_im = loc_w * spatial_w - 0.5; + *(cache_grad_sampling_loc+(threadIdx.x << 1)) = 0; + *(cache_grad_sampling_loc+((threadIdx.x << 1) + 1)) = 0; + *(cache_grad_attn_weight+threadIdx.x)=0; + if (h_im > -1 && w_im > -1 && h_im < spatial_h && w_im < spatial_w) + { + ms_deform_attn_col2im_bilinear( + data_value_ptr, spatial_h, spatial_w, num_heads, channels, h_im, w_im, m_col, c_col, + top_grad, weight, grad_value_ptr, + cache_grad_sampling_loc+(threadIdx.x << 1), cache_grad_attn_weight+threadIdx.x); + } + + __syncthreads(); + if (tid == 0) + { + scalar_t _grad_w=cache_grad_sampling_loc[0], _grad_h=cache_grad_sampling_loc[1], _grad_a=cache_grad_attn_weight[0]; + int sid=2; + for (unsigned int tid = 1; tid < blockSize; ++tid) + { + _grad_w += cache_grad_sampling_loc[sid]; + _grad_h += cache_grad_sampling_loc[sid + 1]; + _grad_a += cache_grad_attn_weight[tid]; + sid += 2; + } + + + *grad_sampling_loc = _grad_w; + *(grad_sampling_loc + 1) = _grad_h; + *grad_attn_weight = _grad_a; + } + __syncthreads(); + + data_weight_ptr += 1; + data_loc_w_ptr += 2; + grad_attn_weight += grad_weight_stride; + grad_sampling_loc += grad_loc_stride; + } + } + } +} + + +template +__global__ void ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v2(const int n, + const scalar_t *grad_col, + const scalar_t *data_value, + const int64_t *data_spatial_shapes, + const int64_t *data_level_start_index, + const scalar_t *data_sampling_loc, + const scalar_t *data_attn_weight, + const int batch_size, + const int spatial_size, + const int num_heads, + const int channels, + const int num_levels, + const int num_query, + const int num_point, + scalar_t *grad_value, + scalar_t *grad_sampling_loc, + scalar_t *grad_attn_weight) +{ + CUDA_KERNEL_LOOP(index, n) + { + __shared__ scalar_t cache_grad_sampling_loc[blockSize * 2]; + __shared__ scalar_t cache_grad_attn_weight[blockSize]; + unsigned int tid = threadIdx.x; + int _temp = index; + const int c_col = _temp % channels; + _temp /= channels; + const int sampling_index = _temp; + const int m_col = _temp % num_heads; + _temp /= num_heads; + const int q_col = _temp % num_query; + _temp /= num_query; + const int b_col = _temp; + + const scalar_t top_grad = grad_col[index]; + + int data_weight_ptr = sampling_index * num_levels * num_point; + int data_loc_w_ptr = data_weight_ptr << 1; + const int grad_sampling_ptr = data_weight_ptr; + grad_sampling_loc += grad_sampling_ptr << 1; + grad_attn_weight += grad_sampling_ptr; + const int grad_weight_stride = 1; + const int grad_loc_stride = 2; + const int qid_stride = num_heads * channels; + const int data_value_ptr_init_offset = b_col * spatial_size * qid_stride; + + for (int l_col=0; l_col < num_levels; ++l_col) + { + const int level_start_id = data_level_start_index[l_col]; + const int spatial_h_ptr = l_col << 1; + const int spatial_h = data_spatial_shapes[spatial_h_ptr]; + const int spatial_w = data_spatial_shapes[spatial_h_ptr + 1]; + const int value_ptr_offset = data_value_ptr_init_offset + level_start_id * qid_stride; + const scalar_t *data_value_ptr = data_value + value_ptr_offset; + scalar_t *grad_value_ptr = grad_value + value_ptr_offset; + + for (int p_col=0; p_col < num_point; ++p_col) + { + const scalar_t loc_w = data_sampling_loc[data_loc_w_ptr]; + const scalar_t loc_h = data_sampling_loc[data_loc_w_ptr + 1]; + const scalar_t weight = data_attn_weight[data_weight_ptr]; + + const scalar_t h_im = loc_h * spatial_h - 0.5; + const scalar_t w_im = loc_w * spatial_w - 0.5; + *(cache_grad_sampling_loc+(threadIdx.x << 1)) = 0; + *(cache_grad_sampling_loc+((threadIdx.x << 1) + 1)) = 0; + *(cache_grad_attn_weight+threadIdx.x)=0; + if (h_im > -1 && w_im > -1 && h_im < spatial_h && w_im < spatial_w) + { + ms_deform_attn_col2im_bilinear( + data_value_ptr, spatial_h, spatial_w, num_heads, channels, h_im, w_im, m_col, c_col, + top_grad, weight, grad_value_ptr, + cache_grad_sampling_loc+(threadIdx.x << 1), cache_grad_attn_weight+threadIdx.x); + } + + __syncthreads(); + + for (unsigned int s=blockSize/2; s>0; s>>=1) + { + if (tid < s) { + const unsigned int xid1 = tid << 1; + const unsigned int xid2 = (tid + s) << 1; + cache_grad_attn_weight[tid] += cache_grad_attn_weight[tid + s]; + cache_grad_sampling_loc[xid1] += cache_grad_sampling_loc[xid2]; + cache_grad_sampling_loc[xid1 + 1] += cache_grad_sampling_loc[xid2 + 1]; + } + __syncthreads(); + } + + if (tid == 0) + { + *grad_sampling_loc = cache_grad_sampling_loc[0]; + *(grad_sampling_loc + 1) = cache_grad_sampling_loc[1]; + *grad_attn_weight = cache_grad_attn_weight[0]; + } + __syncthreads(); + + data_weight_ptr += 1; + data_loc_w_ptr += 2; + grad_attn_weight += grad_weight_stride; + grad_sampling_loc += grad_loc_stride; + } + } + } +} + + +template +__global__ void ms_deformable_col2im_gpu_kernel_shm_reduce_v1(const int n, + const scalar_t *grad_col, + const scalar_t *data_value, + const int64_t *data_spatial_shapes, + const int64_t *data_level_start_index, + const scalar_t *data_sampling_loc, + const scalar_t *data_attn_weight, + const int batch_size, + const int spatial_size, + const int num_heads, + const int channels, + const int num_levels, + const int num_query, + const int num_point, + scalar_t *grad_value, + scalar_t *grad_sampling_loc, + scalar_t *grad_attn_weight) +{ + CUDA_KERNEL_LOOP(index, n) + { + extern __shared__ int _s[]; + scalar_t* cache_grad_sampling_loc = (scalar_t*)_s; + scalar_t* cache_grad_attn_weight = cache_grad_sampling_loc + 2 * blockDim.x; + unsigned int tid = threadIdx.x; + int _temp = index; + const int c_col = _temp % channels; + _temp /= channels; + const int sampling_index = _temp; + const int m_col = _temp % num_heads; + _temp /= num_heads; + const int q_col = _temp % num_query; + _temp /= num_query; + const int b_col = _temp; + + const scalar_t top_grad = grad_col[index]; + + int data_weight_ptr = sampling_index * num_levels * num_point; + int data_loc_w_ptr = data_weight_ptr << 1; + const int grad_sampling_ptr = data_weight_ptr; + grad_sampling_loc += grad_sampling_ptr << 1; + grad_attn_weight += grad_sampling_ptr; + const int grad_weight_stride = 1; + const int grad_loc_stride = 2; + const int qid_stride = num_heads * channels; + const int data_value_ptr_init_offset = b_col * spatial_size * qid_stride; + + for (int l_col=0; l_col < num_levels; ++l_col) + { + const int level_start_id = data_level_start_index[l_col]; + const int spatial_h_ptr = l_col << 1; + const int spatial_h = data_spatial_shapes[spatial_h_ptr]; + const int spatial_w = data_spatial_shapes[spatial_h_ptr + 1]; + const int value_ptr_offset = data_value_ptr_init_offset + level_start_id * qid_stride; + const scalar_t *data_value_ptr = data_value + value_ptr_offset; + scalar_t *grad_value_ptr = grad_value + value_ptr_offset; + + for (int p_col=0; p_col < num_point; ++p_col) + { + const scalar_t loc_w = data_sampling_loc[data_loc_w_ptr]; + const scalar_t loc_h = data_sampling_loc[data_loc_w_ptr + 1]; + const scalar_t weight = data_attn_weight[data_weight_ptr]; + + const scalar_t h_im = loc_h * spatial_h - 0.5; + const scalar_t w_im = loc_w * spatial_w - 0.5; + *(cache_grad_sampling_loc+(threadIdx.x << 1)) = 0; + *(cache_grad_sampling_loc+((threadIdx.x << 1) + 1)) = 0; + *(cache_grad_attn_weight+threadIdx.x)=0; + if (h_im > -1 && w_im > -1 && h_im < spatial_h && w_im < spatial_w) + { + ms_deform_attn_col2im_bilinear( + data_value_ptr, spatial_h, spatial_w, num_heads, channels, h_im, w_im, m_col, c_col, + top_grad, weight, grad_value_ptr, + cache_grad_sampling_loc+(threadIdx.x << 1), cache_grad_attn_weight+threadIdx.x); + } + + __syncthreads(); + if (tid == 0) + { + scalar_t _grad_w=cache_grad_sampling_loc[0], _grad_h=cache_grad_sampling_loc[1], _grad_a=cache_grad_attn_weight[0]; + int sid=2; + for (unsigned int tid = 1; tid < blockDim.x; ++tid) + { + _grad_w += cache_grad_sampling_loc[sid]; + _grad_h += cache_grad_sampling_loc[sid + 1]; + _grad_a += cache_grad_attn_weight[tid]; + sid += 2; + } + + + *grad_sampling_loc = _grad_w; + *(grad_sampling_loc + 1) = _grad_h; + *grad_attn_weight = _grad_a; + } + __syncthreads(); + + data_weight_ptr += 1; + data_loc_w_ptr += 2; + grad_attn_weight += grad_weight_stride; + grad_sampling_loc += grad_loc_stride; + } + } + } +} + +template +__global__ void ms_deformable_col2im_gpu_kernel_shm_reduce_v2(const int n, + const scalar_t *grad_col, + const scalar_t *data_value, + const int64_t *data_spatial_shapes, + const int64_t *data_level_start_index, + const scalar_t *data_sampling_loc, + const scalar_t *data_attn_weight, + const int batch_size, + const int spatial_size, + const int num_heads, + const int channels, + const int num_levels, + const int num_query, + const int num_point, + scalar_t *grad_value, + scalar_t *grad_sampling_loc, + scalar_t *grad_attn_weight) +{ + CUDA_KERNEL_LOOP(index, n) + { + extern __shared__ int _s[]; + scalar_t* cache_grad_sampling_loc = (scalar_t*)_s; + scalar_t* cache_grad_attn_weight = cache_grad_sampling_loc + 2 * blockDim.x; + unsigned int tid = threadIdx.x; + int _temp = index; + const int c_col = _temp % channels; + _temp /= channels; + const int sampling_index = _temp; + const int m_col = _temp % num_heads; + _temp /= num_heads; + const int q_col = _temp % num_query; + _temp /= num_query; + const int b_col = _temp; + + const scalar_t top_grad = grad_col[index]; + + int data_weight_ptr = sampling_index * num_levels * num_point; + int data_loc_w_ptr = data_weight_ptr << 1; + const int grad_sampling_ptr = data_weight_ptr; + grad_sampling_loc += grad_sampling_ptr << 1; + grad_attn_weight += grad_sampling_ptr; + const int grad_weight_stride = 1; + const int grad_loc_stride = 2; + const int qid_stride = num_heads * channels; + const int data_value_ptr_init_offset = b_col * spatial_size * qid_stride; + + for (int l_col=0; l_col < num_levels; ++l_col) + { + const int level_start_id = data_level_start_index[l_col]; + const int spatial_h_ptr = l_col << 1; + const int spatial_h = data_spatial_shapes[spatial_h_ptr]; + const int spatial_w = data_spatial_shapes[spatial_h_ptr + 1]; + const int value_ptr_offset = data_value_ptr_init_offset + level_start_id * qid_stride; + const scalar_t *data_value_ptr = data_value + value_ptr_offset; + scalar_t *grad_value_ptr = grad_value + value_ptr_offset; + + for (int p_col=0; p_col < num_point; ++p_col) + { + const scalar_t loc_w = data_sampling_loc[data_loc_w_ptr]; + const scalar_t loc_h = data_sampling_loc[data_loc_w_ptr + 1]; + const scalar_t weight = data_attn_weight[data_weight_ptr]; + + const scalar_t h_im = loc_h * spatial_h - 0.5; + const scalar_t w_im = loc_w * spatial_w - 0.5; + *(cache_grad_sampling_loc+(threadIdx.x << 1)) = 0; + *(cache_grad_sampling_loc+((threadIdx.x << 1) + 1)) = 0; + *(cache_grad_attn_weight+threadIdx.x)=0; + if (h_im > -1 && w_im > -1 && h_im < spatial_h && w_im < spatial_w) + { + ms_deform_attn_col2im_bilinear( + data_value_ptr, spatial_h, spatial_w, num_heads, channels, h_im, w_im, m_col, c_col, + top_grad, weight, grad_value_ptr, + cache_grad_sampling_loc+(threadIdx.x << 1), cache_grad_attn_weight+threadIdx.x); + } + + __syncthreads(); + + for (unsigned int s=blockDim.x/2, spre=blockDim.x; s>0; s>>=1, spre>>=1) + { + if (tid < s) { + const unsigned int xid1 = tid << 1; + const unsigned int xid2 = (tid + s) << 1; + cache_grad_attn_weight[tid] += cache_grad_attn_weight[tid + s]; + cache_grad_sampling_loc[xid1] += cache_grad_sampling_loc[xid2]; + cache_grad_sampling_loc[xid1 + 1] += cache_grad_sampling_loc[xid2 + 1]; + if (tid + (s << 1) < spre) + { + cache_grad_attn_weight[tid] += cache_grad_attn_weight[tid + (s << 1)]; + cache_grad_sampling_loc[xid1] += cache_grad_sampling_loc[xid2 + (s << 1)]; + cache_grad_sampling_loc[xid1 + 1] += cache_grad_sampling_loc[xid2 + 1 + (s << 1)]; + } + } + __syncthreads(); + } + + if (tid == 0) + { + *grad_sampling_loc = cache_grad_sampling_loc[0]; + *(grad_sampling_loc + 1) = cache_grad_sampling_loc[1]; + *grad_attn_weight = cache_grad_attn_weight[0]; + } + __syncthreads(); + + data_weight_ptr += 1; + data_loc_w_ptr += 2; + grad_attn_weight += grad_weight_stride; + grad_sampling_loc += grad_loc_stride; + } + } + } +} + +template +__global__ void ms_deformable_col2im_gpu_kernel_shm_reduce_v2_multi_blocks(const int n, + const scalar_t *grad_col, + const scalar_t *data_value, + const int64_t *data_spatial_shapes, + const int64_t *data_level_start_index, + const scalar_t *data_sampling_loc, + const scalar_t *data_attn_weight, + const int batch_size, + const int spatial_size, + const int num_heads, + const int channels, + const int num_levels, + const int num_query, + const int num_point, + scalar_t *grad_value, + scalar_t *grad_sampling_loc, + scalar_t *grad_attn_weight) +{ + CUDA_KERNEL_LOOP(index, n) + { + extern __shared__ int _s[]; + scalar_t* cache_grad_sampling_loc = (scalar_t*)_s; + scalar_t* cache_grad_attn_weight = cache_grad_sampling_loc + 2 * blockDim.x; + unsigned int tid = threadIdx.x; + int _temp = index; + const int c_col = _temp % channels; + _temp /= channels; + const int sampling_index = _temp; + const int m_col = _temp % num_heads; + _temp /= num_heads; + const int q_col = _temp % num_query; + _temp /= num_query; + const int b_col = _temp; + + const scalar_t top_grad = grad_col[index]; + + int data_weight_ptr = sampling_index * num_levels * num_point; + int data_loc_w_ptr = data_weight_ptr << 1; + const int grad_sampling_ptr = data_weight_ptr; + grad_sampling_loc += grad_sampling_ptr << 1; + grad_attn_weight += grad_sampling_ptr; + const int grad_weight_stride = 1; + const int grad_loc_stride = 2; + const int qid_stride = num_heads * channels; + const int data_value_ptr_init_offset = b_col * spatial_size * qid_stride; + + for (int l_col=0; l_col < num_levels; ++l_col) + { + const int level_start_id = data_level_start_index[l_col]; + const int spatial_h_ptr = l_col << 1; + const int spatial_h = data_spatial_shapes[spatial_h_ptr]; + const int spatial_w = data_spatial_shapes[spatial_h_ptr + 1]; + const int value_ptr_offset = data_value_ptr_init_offset + level_start_id * qid_stride; + const scalar_t *data_value_ptr = data_value + value_ptr_offset; + scalar_t *grad_value_ptr = grad_value + value_ptr_offset; + + for (int p_col=0; p_col < num_point; ++p_col) + { + const scalar_t loc_w = data_sampling_loc[data_loc_w_ptr]; + const scalar_t loc_h = data_sampling_loc[data_loc_w_ptr + 1]; + const scalar_t weight = data_attn_weight[data_weight_ptr]; + + const scalar_t h_im = loc_h * spatial_h - 0.5; + const scalar_t w_im = loc_w * spatial_w - 0.5; + *(cache_grad_sampling_loc+(threadIdx.x << 1)) = 0; + *(cache_grad_sampling_loc+((threadIdx.x << 1) + 1)) = 0; + *(cache_grad_attn_weight+threadIdx.x)=0; + if (h_im > -1 && w_im > -1 && h_im < spatial_h && w_im < spatial_w) + { + ms_deform_attn_col2im_bilinear( + data_value_ptr, spatial_h, spatial_w, num_heads, channels, h_im, w_im, m_col, c_col, + top_grad, weight, grad_value_ptr, + cache_grad_sampling_loc+(threadIdx.x << 1), cache_grad_attn_weight+threadIdx.x); + } + + __syncthreads(); + + for (unsigned int s=blockDim.x/2, spre=blockDim.x; s>0; s>>=1, spre>>=1) + { + if (tid < s) { + const unsigned int xid1 = tid << 1; + const unsigned int xid2 = (tid + s) << 1; + cache_grad_attn_weight[tid] += cache_grad_attn_weight[tid + s]; + cache_grad_sampling_loc[xid1] += cache_grad_sampling_loc[xid2]; + cache_grad_sampling_loc[xid1 + 1] += cache_grad_sampling_loc[xid2 + 1]; + if (tid + (s << 1) < spre) + { + cache_grad_attn_weight[tid] += cache_grad_attn_weight[tid + (s << 1)]; + cache_grad_sampling_loc[xid1] += cache_grad_sampling_loc[xid2 + (s << 1)]; + cache_grad_sampling_loc[xid1 + 1] += cache_grad_sampling_loc[xid2 + 1 + (s << 1)]; + } + } + __syncthreads(); + } + + if (tid == 0) + { + atomicAdd(grad_sampling_loc, cache_grad_sampling_loc[0]); + atomicAdd(grad_sampling_loc + 1, cache_grad_sampling_loc[1]); + atomicAdd(grad_attn_weight, cache_grad_attn_weight[0]); + } + __syncthreads(); + + data_weight_ptr += 1; + data_loc_w_ptr += 2; + grad_attn_weight += grad_weight_stride; + grad_sampling_loc += grad_loc_stride; + } + } + } +} + + +template +__global__ void ms_deformable_col2im_gpu_kernel_gm(const int n, + const scalar_t *grad_col, + const scalar_t *data_value, + const int64_t *data_spatial_shapes, + const int64_t *data_level_start_index, + const scalar_t *data_sampling_loc, + const scalar_t *data_attn_weight, + const int batch_size, + const int spatial_size, + const int num_heads, + const int channels, + const int num_levels, + const int num_query, + const int num_point, + scalar_t *grad_value, + scalar_t *grad_sampling_loc, + scalar_t *grad_attn_weight) +{ + CUDA_KERNEL_LOOP(index, n) + { + int _temp = index; + const int c_col = _temp % channels; + _temp /= channels; + const int sampling_index = _temp; + const int m_col = _temp % num_heads; + _temp /= num_heads; + const int q_col = _temp % num_query; + _temp /= num_query; + const int b_col = _temp; + + const scalar_t top_grad = grad_col[index]; + + int data_weight_ptr = sampling_index * num_levels * num_point; + int data_loc_w_ptr = data_weight_ptr << 1; + const int grad_sampling_ptr = data_weight_ptr; + grad_sampling_loc += grad_sampling_ptr << 1; + grad_attn_weight += grad_sampling_ptr; + const int grad_weight_stride = 1; + const int grad_loc_stride = 2; + const int qid_stride = num_heads * channels; + const int data_value_ptr_init_offset = b_col * spatial_size * qid_stride; + + for (int l_col=0; l_col < num_levels; ++l_col) + { + const int level_start_id = data_level_start_index[l_col]; + const int spatial_h_ptr = l_col << 1; + const int spatial_h = data_spatial_shapes[spatial_h_ptr]; + const int spatial_w = data_spatial_shapes[spatial_h_ptr + 1]; + const int value_ptr_offset = data_value_ptr_init_offset + level_start_id * qid_stride; + const scalar_t *data_value_ptr = data_value + value_ptr_offset; + scalar_t *grad_value_ptr = grad_value + value_ptr_offset; + + for (int p_col=0; p_col < num_point; ++p_col) + { + const scalar_t loc_w = data_sampling_loc[data_loc_w_ptr]; + const scalar_t loc_h = data_sampling_loc[data_loc_w_ptr + 1]; + const scalar_t weight = data_attn_weight[data_weight_ptr]; + + const scalar_t h_im = loc_h * spatial_h - 0.5; + const scalar_t w_im = loc_w * spatial_w - 0.5; + if (h_im > -1 && w_im > -1 && h_im < spatial_h && w_im < spatial_w) + { + ms_deform_attn_col2im_bilinear_gm( + data_value_ptr, spatial_h, spatial_w, num_heads, channels, h_im, w_im, m_col, c_col, + top_grad, weight, grad_value_ptr, + grad_sampling_loc, grad_attn_weight); + } + data_weight_ptr += 1; + data_loc_w_ptr += 2; + grad_attn_weight += grad_weight_stride; + grad_sampling_loc += grad_loc_stride; + } + } + } +} + + +template +void ms_deformable_im2col_cuda(cudaStream_t stream, + const scalar_t* data_value, + const int64_t* data_spatial_shapes, + const int64_t* data_level_start_index, + const scalar_t* data_sampling_loc, + const scalar_t* data_attn_weight, + const int batch_size, + const int spatial_size, + const int num_heads, + const int channels, + const int num_levels, + const int num_query, + const int num_point, + scalar_t* data_col) +{ + const int num_kernels = batch_size * num_query * num_heads * channels; + const int num_actual_kernels = batch_size * num_query * num_heads * channels; + const int num_threads = CUDA_NUM_THREADS; + ms_deformable_im2col_gpu_kernel + <<>>( + num_kernels, data_value, data_spatial_shapes, data_level_start_index, data_sampling_loc, data_attn_weight, + batch_size, spatial_size, num_heads, channels, num_levels, num_query, num_point, data_col); + + cudaError_t err = cudaGetLastError(); + if (err != cudaSuccess) + { + printf("error in ms_deformable_im2col_cuda: %s\n", cudaGetErrorString(err)); + } + +} + +template +void ms_deformable_col2im_cuda(cudaStream_t stream, + const scalar_t* grad_col, + const scalar_t* data_value, + const int64_t * data_spatial_shapes, + const int64_t * data_level_start_index, + const scalar_t * data_sampling_loc, + const scalar_t * data_attn_weight, + const int batch_size, + const int spatial_size, + const int num_heads, + const int channels, + const int num_levels, + const int num_query, + const int num_point, + scalar_t* grad_value, + scalar_t* grad_sampling_loc, + scalar_t* grad_attn_weight) +{ + const int num_threads = (channels > CUDA_NUM_THREADS)?CUDA_NUM_THREADS:channels; + const int num_kernels = batch_size * num_query * num_heads * channels; + const int num_actual_kernels = batch_size * num_query * num_heads * channels; + if (channels > 1024) + { + if ((channels & 1023) == 0) + { + ms_deformable_col2im_gpu_kernel_shm_reduce_v2_multi_blocks + <<>>( + num_kernels, + grad_col, + data_value, + data_spatial_shapes, + data_level_start_index, + data_sampling_loc, + data_attn_weight, + batch_size, + spatial_size, + num_heads, + channels, + num_levels, + num_query, + num_point, + grad_value, + grad_sampling_loc, + grad_attn_weight); + } + else + { + ms_deformable_col2im_gpu_kernel_gm + <<>>( + num_kernels, + grad_col, + data_value, + data_spatial_shapes, + data_level_start_index, + data_sampling_loc, + data_attn_weight, + batch_size, + spatial_size, + num_heads, + channels, + num_levels, + num_query, + num_point, + grad_value, + grad_sampling_loc, + grad_attn_weight); + } + } + else{ + switch(channels) + { + case 1: + ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v1 + <<>>( + num_kernels, + grad_col, + data_value, + data_spatial_shapes, + data_level_start_index, + data_sampling_loc, + data_attn_weight, + batch_size, + spatial_size, + num_heads, + channels, + num_levels, + num_query, + num_point, + grad_value, + grad_sampling_loc, + grad_attn_weight); + break; + case 2: + ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v1 + <<>>( + num_kernels, + grad_col, + data_value, + data_spatial_shapes, + data_level_start_index, + data_sampling_loc, + data_attn_weight, + batch_size, + spatial_size, + num_heads, + channels, + num_levels, + num_query, + num_point, + grad_value, + grad_sampling_loc, + grad_attn_weight); + break; + case 4: + ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v1 + <<>>( + num_kernels, + grad_col, + data_value, + data_spatial_shapes, + data_level_start_index, + data_sampling_loc, + data_attn_weight, + batch_size, + spatial_size, + num_heads, + channels, + num_levels, + num_query, + num_point, + grad_value, + grad_sampling_loc, + grad_attn_weight); + break; + case 8: + ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v1 + <<>>( + num_kernels, + grad_col, + data_value, + data_spatial_shapes, + data_level_start_index, + data_sampling_loc, + data_attn_weight, + batch_size, + spatial_size, + num_heads, + channels, + num_levels, + num_query, + num_point, + grad_value, + grad_sampling_loc, + grad_attn_weight); + break; + case 16: + ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v1 + <<>>( + num_kernels, + grad_col, + data_value, + data_spatial_shapes, + data_level_start_index, + data_sampling_loc, + data_attn_weight, + batch_size, + spatial_size, + num_heads, + channels, + num_levels, + num_query, + num_point, + grad_value, + grad_sampling_loc, + grad_attn_weight); + break; + case 32: + ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v1 + <<>>( + num_kernels, + grad_col, + data_value, + data_spatial_shapes, + data_level_start_index, + data_sampling_loc, + data_attn_weight, + batch_size, + spatial_size, + num_heads, + channels, + num_levels, + num_query, + num_point, + grad_value, + grad_sampling_loc, + grad_attn_weight); + break; + case 64: + ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v2 + <<>>( + num_kernels, + grad_col, + data_value, + data_spatial_shapes, + data_level_start_index, + data_sampling_loc, + data_attn_weight, + batch_size, + spatial_size, + num_heads, + channels, + num_levels, + num_query, + num_point, + grad_value, + grad_sampling_loc, + grad_attn_weight); + break; + case 128: + ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v2 + <<>>( + num_kernels, + grad_col, + data_value, + data_spatial_shapes, + data_level_start_index, + data_sampling_loc, + data_attn_weight, + batch_size, + spatial_size, + num_heads, + channels, + num_levels, + num_query, + num_point, + grad_value, + grad_sampling_loc, + grad_attn_weight); + break; + case 256: + ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v2 + <<>>( + num_kernels, + grad_col, + data_value, + data_spatial_shapes, + data_level_start_index, + data_sampling_loc, + data_attn_weight, + batch_size, + spatial_size, + num_heads, + channels, + num_levels, + num_query, + num_point, + grad_value, + grad_sampling_loc, + grad_attn_weight); + break; + case 512: + ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v2 + <<>>( + num_kernels, + grad_col, + data_value, + data_spatial_shapes, + data_level_start_index, + data_sampling_loc, + data_attn_weight, + batch_size, + spatial_size, + num_heads, + channels, + num_levels, + num_query, + num_point, + grad_value, + grad_sampling_loc, + grad_attn_weight); + break; + case 1024: + ms_deformable_col2im_gpu_kernel_shm_blocksize_aware_reduce_v2 + <<>>( + num_kernels, + grad_col, + data_value, + data_spatial_shapes, + data_level_start_index, + data_sampling_loc, + data_attn_weight, + batch_size, + spatial_size, + num_heads, + channels, + num_levels, + num_query, + num_point, + grad_value, + grad_sampling_loc, + grad_attn_weight); + break; + default: + if (channels < 64) + { + ms_deformable_col2im_gpu_kernel_shm_reduce_v1 + <<>>( + num_kernels, + grad_col, + data_value, + data_spatial_shapes, + data_level_start_index, + data_sampling_loc, + data_attn_weight, + batch_size, + spatial_size, + num_heads, + channels, + num_levels, + num_query, + num_point, + grad_value, + grad_sampling_loc, + grad_attn_weight); + } + else + { + ms_deformable_col2im_gpu_kernel_shm_reduce_v2 + <<>>( + num_kernels, + grad_col, + data_value, + data_spatial_shapes, + data_level_start_index, + data_sampling_loc, + data_attn_weight, + batch_size, + spatial_size, + num_heads, + channels, + num_levels, + num_query, + num_point, + grad_value, + grad_sampling_loc, + grad_attn_weight); + } + } + } + cudaError_t err = cudaGetLastError(); + if (err != cudaSuccess) + { + printf("error in ms_deformable_col2im_cuda: %s\n", cudaGetErrorString(err)); + } + +} diff --git a/venv/lib/python3.13/site-packages/transformers/kernels/deta/ms_deform_attn.h b/venv/lib/python3.13/site-packages/transformers/kernels/deta/ms_deform_attn.h new file mode 100644 index 0000000000000000000000000000000000000000..119b1fa317d1e5fcfb61a4837e560e9248db05f3 --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/kernels/deta/ms_deform_attn.h @@ -0,0 +1,61 @@ +/*! +************************************************************************************************** +* Deformable DETR +* Copyright (c) 2020 SenseTime. All Rights Reserved. +* Licensed under the Apache License, Version 2.0 [see LICENSE for details] +************************************************************************************************** +* Modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0 +************************************************************************************************** +*/ + +#pragma once + +#include "cpu/ms_deform_attn_cpu.h" + +#ifdef WITH_CUDA +#include "cuda/ms_deform_attn_cuda.h" +#endif + + +at::Tensor +ms_deform_attn_forward( + const at::Tensor &value, + const at::Tensor &spatial_shapes, + const at::Tensor &level_start_index, + const at::Tensor &sampling_loc, + const at::Tensor &attn_weight, + const int im2col_step) +{ + if (value.type().is_cuda()) + { +#ifdef WITH_CUDA + return ms_deform_attn_cuda_forward( + value, spatial_shapes, level_start_index, sampling_loc, attn_weight, im2col_step); +#else + AT_ERROR("Not compiled with GPU support"); +#endif + } + AT_ERROR("Not implemented on the CPU"); +} + +std::vector +ms_deform_attn_backward( + const at::Tensor &value, + const at::Tensor &spatial_shapes, + const at::Tensor &level_start_index, + const at::Tensor &sampling_loc, + const at::Tensor &attn_weight, + const at::Tensor &grad_output, + const int im2col_step) +{ + if (value.type().is_cuda()) + { +#ifdef WITH_CUDA + return ms_deform_attn_cuda_backward( + value, spatial_shapes, level_start_index, sampling_loc, attn_weight, grad_output, im2col_step); +#else + AT_ERROR("Not compiled with GPU support"); +#endif + } + AT_ERROR("Not implemented on the CPU"); +} diff --git a/venv/lib/python3.13/site-packages/transformers/kernels/deta/vision.cpp b/venv/lib/python3.13/site-packages/transformers/kernels/deta/vision.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6ce3875568b9ba8d660c90acc805077cca98f891 --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/kernels/deta/vision.cpp @@ -0,0 +1,16 @@ +/*! +************************************************************************************************** +* Deformable DETR +* Copyright (c) 2020 SenseTime. All Rights Reserved. +* Licensed under the Apache License, Version 2.0 [see LICENSE for details] +************************************************************************************************** +* Modified from https://github.com/chengdazhi/Deformable-Convolution-V2-PyTorch/tree/pytorch_1.0.0 +************************************************************************************************** +*/ + +#include "ms_deform_attn.h" + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("ms_deform_attn_forward", &ms_deform_attn_forward, "ms_deform_attn_forward"); + m.def("ms_deform_attn_backward", &ms_deform_attn_backward, "ms_deform_attn_backward"); +} \ No newline at end of file diff --git a/venv/lib/python3.13/site-packages/transformers/kernels/falcon_mamba/__init__.py b/venv/lib/python3.13/site-packages/transformers/kernels/falcon_mamba/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..da88e3394f653369a7443245c67dcbe57f2ed23e --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/kernels/falcon_mamba/__init__.py @@ -0,0 +1,15 @@ +# coding=utf-8 +# Copyright 2024 Tri Dao, Albert Gu, Technological Innovation Institute and HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from .selective_scan_with_ln_interface import mamba_inner_fn diff --git a/venv/lib/python3.13/site-packages/transformers/kernels/falcon_mamba/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/kernels/falcon_mamba/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ca2cbd437088746c25d77775686e0b57ceb722c5 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/kernels/falcon_mamba/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/kernels/falcon_mamba/__pycache__/selective_scan_with_ln_interface.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/kernels/falcon_mamba/__pycache__/selective_scan_with_ln_interface.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..92334ff8c0ea31fb99c23c18dc7b5cd64911d3a2 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/kernels/falcon_mamba/__pycache__/selective_scan_with_ln_interface.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/kernels/falcon_mamba/selective_scan_with_ln_interface.py b/venv/lib/python3.13/site-packages/transformers/kernels/falcon_mamba/selective_scan_with_ln_interface.py new file mode 100644 index 0000000000000000000000000000000000000000..4a74986a81a13f9428eab353de5b61a4d101972d --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/kernels/falcon_mamba/selective_scan_with_ln_interface.py @@ -0,0 +1,525 @@ +# coding=utf-8 +# Copyright 2024 Tri Dao, Albert Gu, Technological Innovation Institute and HuggingFace Inc. team. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# Original code from: https://github.com/state-spaces/mamba/blob/main/mamba_ssm/ops/selective_scan_interface.py + +import torch +import torch.nn.functional as F +from einops import rearrange, repeat +from torch.cuda.amp import custom_bwd, custom_fwd + + +try: + import causal_conv1d_cuda +except ImportError: + causal_conv1d_cuda = None + +import mamba_ssm +import selective_scan_cuda + + +# For BC for old mamba-ssm versions: https://github.com/huggingface/transformers/pull/33195#discussion_r1736401127 +if hasattr(mamba_ssm.ops.triton, "layernorm"): + from mamba_ssm.ops.triton.layernorm import _layer_norm_fwd +else: + from mamba_ssm.ops.triton.layer_norm import _layer_norm_fwd + + +class SelectiveScanFn(torch.autograd.Function): + @staticmethod + def forward( + ctx, u, delta, A, B, C, D=None, z=None, delta_bias=None, delta_softplus=False, return_last_state=False + ): + if u.stride(-1) != 1: + u = u.contiguous() + if delta.stride(-1) != 1: + delta = delta.contiguous() + if D is not None: + D = D.contiguous() + if B.stride(-1) != 1: + B = B.contiguous() + if C.stride(-1) != 1: + C = C.contiguous() + if z is not None and z.stride(-1) != 1: + z = z.contiguous() + if B.dim() == 3: + B = rearrange(B, "b dstate l -> b 1 dstate l") + ctx.squeeze_B = True + if C.dim() == 3: + C = rearrange(C, "b dstate l -> b 1 dstate l") + ctx.squeeze_C = True + out, x, *rest = selective_scan_cuda.fwd(u, delta, A, B, C, D, z, delta_bias, delta_softplus) + ctx.delta_softplus = delta_softplus + ctx.has_z = z is not None + last_state = x[:, :, -1, 1::2] # (batch, dim, dstate) + if not ctx.has_z: + ctx.save_for_backward(u, delta, A, B, C, D, delta_bias, x) + return out if not return_last_state else (out, last_state) + else: + ctx.save_for_backward(u, delta, A, B, C, D, z, delta_bias, x, out) + out_z = rest[0] + return out_z if not return_last_state else (out_z, last_state) + + @staticmethod + def backward(ctx, dout, *args): + if not ctx.has_z: + u, delta, A, B, C, D, delta_bias, x = ctx.saved_tensors + z = None + out = None + else: + u, delta, A, B, C, D, z, delta_bias, x, out = ctx.saved_tensors + if dout.stride(-1) != 1: + dout = dout.contiguous() + # The kernel supports passing in a pre-allocated dz (e.g., in case we want to fuse the + # backward of selective_scan_cuda with the backward of chunk). + # Here we just pass in None and dz will be allocated in the C++ code. + du, ddelta, dA, dB, dC, dD, ddelta_bias, *rest = selective_scan_cuda.bwd( + u, + delta, + A, + B, + C, + D, + z, + delta_bias, + dout, + x, + out, + None, + ctx.delta_softplus, + False, # option to recompute out_z, not used here + ) + dz = rest[0] if ctx.has_z else None + dB = dB.squeeze(1) if getattr(ctx, "squeeze_B", False) else dB + dC = dC.squeeze(1) if getattr(ctx, "squeeze_C", False) else dC + return ( + du, + ddelta, + dA, + dB, + dC, + dD if D is not None else None, + dz, + ddelta_bias if delta_bias is not None else None, + None, + None, + ) + + +def rms_norm_forward( + x, + weight, + bias, + eps=1e-6, + is_rms_norm=True, +): + # x (b l) d + if x.stride(-1) != 1: + x = x.contiguous() + weight = weight.contiguous() + if bias is not None: + bias = bias.contiguous() + y = _layer_norm_fwd(x, weight, bias, eps, None, residual_dtype=None, is_rms_norm=is_rms_norm)[0] + # y (b l) d + return y + + +def selective_scan_fn( + u, delta, A, B, C, D=None, z=None, delta_bias=None, delta_softplus=False, return_last_state=False +): + """if return_last_state is True, returns (out, last_state) + last_state has shape (batch, dim, dstate). Note that the gradient of the last state is + not considered in the backward pass. + """ + return SelectiveScanFn.apply(u, delta, A, B, C, D, z, delta_bias, delta_softplus, return_last_state) + + +def selective_scan_ref( + u, delta, A, B, C, D=None, z=None, delta_bias=None, delta_softplus=False, return_last_state=False +): + """ + u: r(B D L) + delta: r(B D L) + A: c(D N) or r(D N) + B: c(D N) or r(B N L) or r(B N 2L) or r(B G N L) or (B G N L) + C: c(D N) or r(B N L) or r(B N 2L) or r(B G N L) or (B G N L) + D: r(D) + z: r(B D L) + delta_bias: r(D), fp32 + + out: r(B D L) + last_state (optional): r(B D dstate) or c(B D dstate) + """ + dtype_in = u.dtype + u = u.float() + delta = delta.float() + if delta_bias is not None: + delta = delta + delta_bias[..., None].float() + if delta_softplus: + delta = F.softplus(delta) + batch, dim, dstate = u.shape[0], A.shape[0], A.shape[1] + is_variable_B = B.dim() >= 3 + is_variable_C = C.dim() >= 3 + if A.is_complex(): + if is_variable_B: + B = torch.view_as_complex(rearrange(B.float(), "... (L two) -> ... L two", two=2)) + if is_variable_C: + C = torch.view_as_complex(rearrange(C.float(), "... (L two) -> ... L two", two=2)) + else: + B = B.float() + C = C.float() + x = A.new_zeros((batch, dim, dstate)) + ys = [] + deltaA = torch.exp(torch.einsum("bdl,dn->bdln", delta, A)) + if not is_variable_B: + deltaB_u = torch.einsum("bdl,dn,bdl->bdln", delta, B, u) + else: + if B.dim() == 3: + deltaB_u = torch.einsum("bdl,bnl,bdl->bdln", delta, B, u) + else: + B = repeat(B, "B G N L -> B (G H) N L", H=dim // B.shape[1]) + deltaB_u = torch.einsum("bdl,bdnl,bdl->bdln", delta, B, u) + if is_variable_C and C.dim() == 4: + C = repeat(C, "B G N L -> B (G H) N L", H=dim // C.shape[1]) + last_state = None + for i in range(u.shape[2]): + x = deltaA[:, :, i] * x + deltaB_u[:, :, i] + if not is_variable_C: + y = torch.einsum("bdn,dn->bd", x, C) + else: + if C.dim() == 3: + y = torch.einsum("bdn,bn->bd", x, C[:, :, i]) + else: + y = torch.einsum("bdn,bdn->bd", x, C[:, :, :, i]) + if i == u.shape[2] - 1: + last_state = x + if y.is_complex(): + y = y.real * 2 + ys.append(y) + y = torch.stack(ys, dim=2) # (batch dim L) + out = y if D is None else y + u * rearrange(D, "d -> d 1") + if z is not None: + out = out * F.silu(z) + out = out.to(dtype=dtype_in) + return out if not return_last_state else (out, last_state) + + +class MambaInnerFn(torch.autograd.Function): + @staticmethod + @custom_fwd + def forward( + ctx, + xz, + conv1d_weight, + conv1d_bias, + x_proj_weight, + delta_proj_weight, + out_proj_weight, + out_proj_bias, + A, + B=None, + C=None, + D=None, + delta_bias=None, + B_proj_bias=None, + C_proj_bias=None, + delta_softplus=True, + checkpoint_lvl=1, + b_rms_weight=None, + c_rms_weight=None, + dt_rms_weight=None, + b_c_dt_rms_eps=1e-6, + ): + """ + xz: (batch, dim, seqlen) + """ + assert causal_conv1d_cuda is not None, "causal_conv1d_cuda is not available. Please install causal-conv1d." + assert checkpoint_lvl in [0, 1] + L = xz.shape[-1] + delta_rank = delta_proj_weight.shape[1] + d_state = A.shape[-1] * (1 if not A.is_complex() else 2) + if torch.is_autocast_enabled(): + x_proj_weight = x_proj_weight.to(dtype=torch.get_autocast_gpu_dtype()) + delta_proj_weight = delta_proj_weight.to(dtype=torch.get_autocast_gpu_dtype()) + out_proj_weight = out_proj_weight.to(dtype=torch.get_autocast_gpu_dtype()) + out_proj_bias = ( + out_proj_bias.to(dtype=torch.get_autocast_gpu_dtype()) if out_proj_bias is not None else None + ) + if xz.stride(-1) != 1: + xz = xz.contiguous() + conv1d_weight = rearrange(conv1d_weight, "d 1 w -> d w") + x, z = xz.chunk(2, dim=1) + conv1d_bias = conv1d_bias.contiguous() if conv1d_bias is not None else None + conv1d_out = causal_conv1d_cuda.causal_conv1d_fwd(x, conv1d_weight, conv1d_bias, None, None, None, True) + # We're being very careful here about the layout, to avoid extra transposes. + # We want delta to have d as the slowest moving dimension + # and L as the fastest moving dimension, since those are what the ssm_scan kernel expects. + x_dbl = F.linear(rearrange(conv1d_out, "b d l -> (b l) d"), x_proj_weight) # (bl d) + delta = rearrange(delta_proj_weight @ x_dbl[:, :delta_rank].t(), "d (b l) -> b d l", l=L) + ctx.is_variable_B = B is None + ctx.is_variable_C = C is None + ctx.B_proj_bias_is_None = B_proj_bias is None + ctx.C_proj_bias_is_None = C_proj_bias is None + if B is None: # variable B + B = x_dbl[:, delta_rank : delta_rank + d_state] # (bl dstate) + if B_proj_bias is not None: + B = B + B_proj_bias.to(dtype=B.dtype) + if not A.is_complex(): + # B = rearrange(B, "(b l) dstate -> b dstate l", l=L).contiguous() + B = rearrange(B, "(b l) dstate -> b 1 dstate l", l=L).contiguous() + else: + B = rearrange(B, "(b l) (dstate two) -> b 1 dstate (l two)", l=L, two=2).contiguous() + else: + if B.stride(-1) != 1: + B = B.contiguous() + if C is None: # variable C + C = x_dbl[:, -d_state:] # (bl dstate) + if C_proj_bias is not None: + C = C + C_proj_bias.to(dtype=C.dtype) + if not A.is_complex(): + # C = rearrange(C, "(b l) dstate -> b dstate l", l=L).contiguous() + C = rearrange(C, "(b l) dstate -> b 1 dstate l", l=L).contiguous() + else: + C = rearrange(C, "(b l) (dstate two) -> b 1 dstate (l two)", l=L, two=2).contiguous() + else: + if C.stride(-1) != 1: + C = C.contiguous() + if D is not None: + D = D.contiguous() + + if b_rms_weight is not None: + B = rearrange(B, "b 1 dstate l -> (b l) dstate", l=L).contiguous() + B = rms_norm_forward(B, b_rms_weight, bias=None, eps=b_c_dt_rms_eps) + B = rearrange(B, "(b l) dstate -> b 1 dstate l", l=L).contiguous() + if c_rms_weight is not None: + C = rearrange(C, "b 1 dstate l -> (b l) dstate", l=L).contiguous() + C = rms_norm_forward(C, c_rms_weight, bias=None, eps=b_c_dt_rms_eps) + C = rearrange(C, "(b l) dstate -> b 1 dstate l", l=L).contiguous() + if dt_rms_weight is not None: + delta = rearrange(delta, "b d l -> (b l) d", l=L).contiguous() + delta = rms_norm_forward(delta, dt_rms_weight, bias=None, eps=b_c_dt_rms_eps) + delta = rearrange(delta, "(b l) d -> b d l", l=L).contiguous() + + out, scan_intermediates, out_z = selective_scan_cuda.fwd( + conv1d_out, delta, A, B, C, D, z, delta_bias, delta_softplus + ) + ctx.delta_softplus = delta_softplus + ctx.out_proj_bias_is_None = out_proj_bias is None + ctx.checkpoint_lvl = checkpoint_lvl + ctx.b_rms_weight = b_rms_weight + ctx.c_rms_weight = c_rms_weight + ctx.dt_rms_weight = dt_rms_weight + ctx.b_c_dt_rms_eps = b_c_dt_rms_eps + if checkpoint_lvl >= 1: # Will recompute conv1d_out and delta in the backward pass + conv1d_out, delta = None, None + ctx.save_for_backward( + xz, + conv1d_weight, + conv1d_bias, + x_dbl, + x_proj_weight, + delta_proj_weight, + out_proj_weight, + conv1d_out, + delta, + A, + B, + C, + D, + delta_bias, + scan_intermediates, + b_rms_weight, + c_rms_weight, + dt_rms_weight, + out, + ) + return F.linear(rearrange(out_z, "b d l -> b l d"), out_proj_weight, out_proj_bias) + + @staticmethod + @custom_bwd + def backward(ctx, dout): + # dout: (batch, seqlen, dim) + assert causal_conv1d_cuda is not None, "causal_conv1d_cuda is not available. Please install causal-conv1d." + ( + xz, + conv1d_weight, + conv1d_bias, + x_dbl, + x_proj_weight, + delta_proj_weight, + out_proj_weight, + conv1d_out, + delta, + A, + B, + C, + D, + delta_bias, + scan_intermediates, + b_rms_weight, + c_rms_weight, + dt_rms_weight, + out, + ) = ctx.saved_tensors + L = xz.shape[-1] + delta_rank = delta_proj_weight.shape[1] + d_state = A.shape[-1] * (1 if not A.is_complex() else 2) + x, z = xz.chunk(2, dim=1) + if dout.stride(-1) != 1: + dout = dout.contiguous() + if ctx.checkpoint_lvl == 1: + conv1d_out = causal_conv1d_cuda.causal_conv1d_fwd(x, conv1d_weight, conv1d_bias, None, None, None, True) + delta = rearrange(delta_proj_weight @ x_dbl[:, :delta_rank].t(), "d (b l) -> b d l", l=L) + if dt_rms_weight is not None: + delta = rearrange(delta, "b d l -> (b l) d", l=L).contiguous() + delta = rms_norm_forward(delta, ctx.dt_rms_weight, None, ctx.b_c_dt_rms_eps) + delta = rearrange(delta, "(b l) d -> b d l", l=L).contiguous() + if b_rms_weight is not None: + # Recompute & RMSNorm B + B = rearrange(B, "b 1 dstate l -> (b l) dstate", l=L).contiguous() + B = rms_norm_forward(B, ctx.b_rms_weight, None, ctx.b_c_dt_rms_eps) + B = rearrange(B, "(b l) dstate -> b 1 dstate l", l=L).contiguous() + if c_rms_weight is not None: + # Recompute & RMSNorm C + C = rearrange(C, "b 1 dstate l -> (b l) dstate", l=L).contiguous() + C = rms_norm_forward(C, ctx.c_rms_weight, None, ctx.b_c_dt_rms_eps) + C = rearrange(C, "(b l) dstate -> b 1 dstate l", l=L).contiguous() + + # The kernel supports passing in a pre-allocated dz (e.g., in case we want to fuse the + # backward of selective_scan_cuda with the backward of chunk). + dxz = torch.empty_like(xz) # (batch, dim, seqlen) + dx, dz = dxz.chunk(2, dim=1) + dout = rearrange(dout, "b l e -> e (b l)") + dout_y = rearrange(out_proj_weight.t() @ dout, "d (b l) -> b d l", l=L) + dconv1d_out, ddelta, dA, dB, dC, dD, ddelta_bias, dz, out_z = selective_scan_cuda.bwd( + conv1d_out, + delta, + A, + B, + C, + D, + z, + delta_bias, + dout_y, + scan_intermediates, + out, + dz, + ctx.delta_softplus, + True, # option to recompute out_z + ) + dout_proj_weight = torch.einsum("eB,dB->ed", dout, rearrange(out_z, "b d l -> d (b l)")) + dout_proj_bias = dout.sum(dim=(0, 1)) if not ctx.out_proj_bias_is_None else None + dD = dD if D is not None else None + dx_dbl = torch.empty_like(x_dbl) + dB_proj_bias = None + if ctx.is_variable_B: + if not A.is_complex(): + dB = rearrange(dB, "b 1 dstate l -> (b l) dstate").contiguous() + else: + dB = rearrange(dB, "b 1 dstate (l two) -> (b l) (dstate two)", two=2).contiguous() + dB_proj_bias = dB.sum(0) if not ctx.B_proj_bias_is_None else None + dx_dbl[:, delta_rank : delta_rank + d_state] = dB # (bl d) + dB = None + dC_proj_bias = None + if ctx.is_variable_C: + if not A.is_complex(): + dC = rearrange(dC, "b 1 dstate l -> (b l) dstate").contiguous() + else: + dC = rearrange(dC, "b 1 dstate (l two) -> (b l) (dstate two)", two=2).contiguous() + dC_proj_bias = dC.sum(0) if not ctx.C_proj_bias_is_None else None + dx_dbl[:, -d_state:] = dC # (bl d) + dC = None + ddelta = rearrange(ddelta, "b d l -> d (b l)") + ddelta_proj_weight = torch.einsum("dB,Br->dr", ddelta, x_dbl[:, :delta_rank]) + dx_dbl[:, :delta_rank] = torch.einsum("dB,dr->Br", ddelta, delta_proj_weight) + dconv1d_out = rearrange(dconv1d_out, "b d l -> d (b l)") + dx_proj_weight = torch.einsum("Br,Bd->rd", dx_dbl, rearrange(conv1d_out, "b d l -> (b l) d")) + dconv1d_out = torch.addmm(dconv1d_out, x_proj_weight.t(), dx_dbl.t(), out=dconv1d_out) + dconv1d_out = rearrange(dconv1d_out, "d (b l) -> b d l", b=x.shape[0], l=x.shape[-1]) + # The kernel supports passing in a pre-allocated dx (e.g., in case we want to fuse the + # backward of conv1d with the backward of chunk). + dx, dconv1d_weight, dconv1d_bias, *_ = causal_conv1d_cuda.causal_conv1d_bwd( + x, conv1d_weight, conv1d_bias, dconv1d_out, None, None, None, dx, False, True + ) + dconv1d_bias = dconv1d_bias if conv1d_bias is not None else None + dconv1d_weight = rearrange(dconv1d_weight, "d w -> d 1 w") + return ( + dxz, + dconv1d_weight, + dconv1d_bias, + dx_proj_weight, + ddelta_proj_weight, + dout_proj_weight, + dout_proj_bias, + dA, + dB, + dC, + dD, + ddelta_bias if delta_bias is not None else None, + # 6-None are delta_softplus, checkpoint_lvl, b_rms_weight, c_rms_weight, dt_rms_weight, b_c_dt_rms_eps + dB_proj_bias, + dC_proj_bias, + None, + None, + None, + None, + None, + None, + ) + + +def mamba_inner_fn( + xz, + conv1d_weight, + conv1d_bias, + x_proj_weight, + delta_proj_weight, + out_proj_weight, + out_proj_bias, + A, + B=None, + C=None, + D=None, + delta_bias=None, + B_proj_bias=None, + C_proj_bias=None, + delta_softplus=True, + checkpoint_lvl=1, + b_rms_weight=None, + c_rms_weight=None, + dt_rms_weight=None, + b_c_dt_rms_eps=1e-6, +): + return MambaInnerFn.apply( + xz, + conv1d_weight, + conv1d_bias, + x_proj_weight, + delta_proj_weight, + out_proj_weight, + out_proj_bias, + A, + B, + C, + D, + delta_bias, + B_proj_bias, + C_proj_bias, + delta_softplus, + checkpoint_lvl, + b_rms_weight, + c_rms_weight, + dt_rms_weight, + b_c_dt_rms_eps, + ) diff --git a/venv/lib/python3.13/site-packages/transformers/kernels/mra/cuda_kernel.cu b/venv/lib/python3.13/site-packages/transformers/kernels/mra/cuda_kernel.cu new file mode 100644 index 0000000000000000000000000000000000000000..87ed89052873813153786bd416a981d3e5279af9 --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/kernels/mra/cuda_kernel.cu @@ -0,0 +1,383 @@ +#include "cuda_kernel.h" + +////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////// + +__global__ void index_max_cuda_kernel( + float *index_vals, // [batch_size, 32, num_block] + int *indices, // [batch_size, num_block] + float *max_vals, // [batch_size, A_num_block * 32] + float *max_vals_scatter, // [batch_size, 32, num_block] + long batch_size, + long A_num_block, + long B_num_block, + long num_block +) { + + long batch_idx = blockIdx.x; + + long thread_idx = threadIdx.x; + long num_thread = blockDim.x; + + extern __shared__ float buffer[]; + int *max_buffer = (int*)buffer; + + for (int i = 0; i < A_num_block * 32; i = i + num_thread) { + int idx = i + thread_idx; + if (idx < A_num_block * 32) { + max_buffer[idx] = -1e8; + } + } + __syncthreads(); + + int *indices_pt = &indices[batch_idx * num_block]; + float *index_vals_pt = &index_vals[batch_idx * num_block * 32]; + + for (int idx_start = 0; idx_start < 32 * num_block; idx_start = idx_start + num_thread) { + int idx = idx_start + thread_idx; + int A_block_idx = indices_pt[idx % num_block] / B_num_block; + atomicMax(&max_buffer[A_block_idx * 32 + idx / num_block], (int)(index_vals_pt[idx] * 1000)); + } + __syncthreads(); + + float *max_vals_pt = &max_vals[batch_idx * A_num_block * 32]; + for (int i = 0; i < A_num_block * 32; i = i + num_thread) { + int idx = i + thread_idx; + if (idx < A_num_block * 32) { + max_vals_pt[idx] = (float)max_buffer[idx] / 1000.; + } + } + + float *max_vals_scatter_pt = &max_vals_scatter[batch_idx * num_block * 32]; + for (int idx_start = 0; idx_start < 32 * num_block; idx_start = idx_start + num_thread) { + int idx = idx_start + thread_idx; + int A_block_idx = indices_pt[idx % num_block] / B_num_block; + max_vals_scatter_pt[idx] = (float)max_buffer[A_block_idx * 32 + idx / num_block] / 1000.; + } + +} + +__global__ void mm_to_sparse_cuda_kernel( + float *dense_A, // [batch_size, A_num_block, dim, 32] + float *dense_B, // [batch_size, B_num_block, dim, 32] + int *indices, // [batch_size, num_block] + float *sparse_C, // [batch_size, num_block, 32, 32] + long batch_size, + long A_num_block, + long B_num_block, + long dim, + long num_block +) { + + long batch_idx = blockIdx.y; + long block_idx = blockIdx.x * blockDim.y + threadIdx.y; + + long thread_idx = threadIdx.x; + + __shared__ float buffer[4096]; + float *A_buffer = &buffer[threadIdx.y * 1024]; // [2, 8, 32] + float *B_buffer = &buffer[threadIdx.y * 1024 + 512]; // [2, 8, 32] + + long batch_idx__block_idx = batch_idx * num_block + block_idx; + + long AB_block_idx = indices[batch_idx__block_idx]; + float *dense_A_pt = &dense_A[(batch_idx * A_num_block + AB_block_idx / B_num_block) * dim * 32]; + float *dense_B_pt = &dense_B[(batch_idx * B_num_block + AB_block_idx % B_num_block) * dim * 32]; + + int reg_1_idx = thread_idx / 8; // [0000000011111111222222223333333344444444555555556666666677777777] + int reg_2_idx = thread_idx % 8; // [0123456701234567012345670123456701234567012345670123456701234567] + + float reg_1[8]; + float reg_2[8]; + + float reg_array[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + #pragma unroll + for (int i = 0; i < 4; i++) { + A_buffer[i * 64 + thread_idx] = dense_A_pt[i * 64 + thread_idx]; + B_buffer[i * 64 + thread_idx] = dense_B_pt[i * 64 + thread_idx]; + } + + __syncthreads(); + + #pragma unroll + for (int i = 0; i < 4; i++) { + reg_1[i] = A_buffer[reg_1_idx * 4 + i]; + reg_2[i] = B_buffer[reg_2_idx * 4 + i]; + } + + for (int dim_stride = 1; dim_stride < (dim / 8); dim_stride++) { + + #pragma unroll + for (int i = 0; i < 4; i++) { + A_buffer[(dim_stride % 2) * 256 + i * 64 + thread_idx] = dense_A_pt[dim_stride * 256 + i * 64 + thread_idx]; + B_buffer[(dim_stride % 2) * 256 + i * 64 + thread_idx] = dense_B_pt[dim_stride * 256 + i * 64 + thread_idx]; + } + + #pragma unroll + for (int mini_dim_idx = 1; mini_dim_idx < 8; mini_dim_idx++) { + #pragma unroll + for (int i = 0; i < 4; i++) { + reg_1[(mini_dim_idx % 2) * 4 + i] = A_buffer[((dim_stride - 1) % 2) * 256 + mini_dim_idx * 32 + reg_1_idx * 4 + i]; + reg_2[(mini_dim_idx % 2) * 4 + i] = B_buffer[((dim_stride - 1) % 2) * 256 + mini_dim_idx * 32 + reg_2_idx * 4 + i]; + } + #pragma unroll + for (int i = 0; i < 4; i++) { + #pragma unroll + for (int j = 0; j < 4; j++) { + reg_array[i * 4 + j] += reg_1[((mini_dim_idx - 1) % 2) * 4 + i] * reg_2[((mini_dim_idx - 1) % 2) * 4 + j]; + } + } + } + + __syncthreads(); + + #pragma unroll + for (int i = 0; i < 4; i++) { + reg_1[i] = A_buffer[(dim_stride % 2) * 256 + reg_1_idx * 4 + i]; + reg_2[i] = B_buffer[(dim_stride % 2) * 256 + reg_2_idx * 4 + i]; + } + + #pragma unroll + for (int i = 0; i < 4; i++) { + #pragma unroll + for (int j = 0; j < 4; j++) { + reg_array[i * 4 + j] += reg_1[4 + i] * reg_2[4 + j]; + } + } + + } + + #pragma unroll + for (int mini_dim_idx = 1; mini_dim_idx < 8; mini_dim_idx++) { + #pragma unroll + for (int i = 0; i < 4; i++) { + reg_1[(mini_dim_idx % 2) * 4 + i] = A_buffer[256 + mini_dim_idx * 32 + reg_1_idx * 4 + i]; + reg_2[(mini_dim_idx % 2) * 4 + i] = B_buffer[256 + mini_dim_idx * 32 + reg_2_idx * 4 + i]; + } + #pragma unroll + for (int i = 0; i < 4; i++) { + #pragma unroll + for (int j = 0; j < 4; j++) { + reg_array[i * 4 + j] += reg_1[((mini_dim_idx - 1) % 2) * 4 + i] * reg_2[((mini_dim_idx - 1) % 2) * 4 + j]; + } + } + } + #pragma unroll + for (int i = 0; i < 4; i++) { + #pragma unroll + for (int j = 0; j < 4; j++) { + reg_array[i * 4 + j] += reg_1[4 + i] * reg_2[4 + j]; + } + } + __syncthreads(); + + float *C_buffer = &buffer[threadIdx.y * 1024]; // [32, 32] + + #pragma unroll + for (int i = 0; i < 4; i++) { + #pragma unroll + for (int j = 0; j < 4; j++) { + C_buffer[(reg_2_idx * 4 + j) * 32 + reg_1_idx * 4 + i] = reg_array[i * 4 + j]; + } + } + __syncthreads(); + + float *sparse_C_pt = &sparse_C[batch_idx__block_idx * 1024]; + + #pragma unroll + for (int i = 0; i < 16; i++) { + sparse_C_pt[i * 64 + thread_idx] = C_buffer[i * 64 + thread_idx]; + } + +} + +__global__ void sparse_dense_mm_cuda_kernel( + float *sparse_A, // [batch_size, num_block, 32, 32] + int *indices, // [batch_size, num_block] + float *dense_B, // [batch_size, B_num_block, dim, 32] + float *dense_C, // [batch_size, A_num_block, dim, 32] + long batch_size, + long A_num_block, + long B_num_block, + long dim, + long num_block +) { + + long batch_idx = blockIdx.y; + long block_idx = blockIdx.x * blockDim.y + threadIdx.y; + + long thread_idx = threadIdx.x; + + __shared__ float buffer[6144]; + float *A_buffer = &buffer[threadIdx.y * 3072]; // [32, 32] + float *B_buffer = &buffer[threadIdx.y * 3072 + 1024]; // [32, 64] + + long batch_idx__block_idx = batch_idx * num_block + block_idx; + + float *sparse_A_pt = &sparse_A[batch_idx__block_idx * 1024]; + #pragma unroll + for (int i = 0; i < 8; i++) { + A_buffer[i * 128 + thread_idx] = sparse_A_pt[i * 128 + thread_idx]; + } + + long AB_block_idx = indices[batch_idx__block_idx]; + float *dense_B_pt = &dense_B[(batch_idx * B_num_block + AB_block_idx % B_num_block) * 32 * dim]; + float *dense_C_pt = &dense_C[(batch_idx * A_num_block + AB_block_idx / B_num_block) * 32 * dim]; + + // [0000000011111111222222223333333344444444555555556666666677777777] + // [0123456701234567012345670123456701234567012345670123456701234567] + int reg_1_idx = thread_idx / 8; + int reg_2_idx = thread_idx % 8; + + float reg_1[8]; + float reg_2[8]; + + float reg_array[16]; + + for (int dim_stride = 0; dim_stride < dim; dim_stride = dim_stride + 64) { + + #pragma unroll + for (int i = 0; i < 16; i++) { + B_buffer[i * 128 + thread_idx] = dense_B_pt[dim_stride * 32 + i * 128 + thread_idx]; + } + + #pragma unroll + for (int i = 0; i < 16; i++) { + reg_array[i] = 0; + } + + __syncthreads(); + + #pragma unroll + for (int i = 0; i < 4; i++) { + reg_1[i] = B_buffer[(reg_1_idx * 4 + i) * 32]; + reg_2[i] = A_buffer[reg_2_idx * 4 + i]; + } + + #pragma unroll + for (int mini_dim_idx = 1; mini_dim_idx < 32; mini_dim_idx++) { + #pragma unroll + for (int i = 0; i < 4; i++) { + reg_1[(mini_dim_idx % 2) * 4 + i] = B_buffer[(reg_1_idx * 4 + i) * 32 + mini_dim_idx]; + reg_2[(mini_dim_idx % 2) * 4 + i] = A_buffer[mini_dim_idx * 32 + reg_2_idx * 4 + i]; + } + #pragma unroll + for (int i = 0; i < 4; i++) { + #pragma unroll + for (int j = 0; j < 4; j++) { + reg_array[i * 4 + j] += reg_1[((mini_dim_idx - 1) % 2) * 4 + i] * reg_2[((mini_dim_idx - 1) % 2) * 4 + j]; + } + } + } + + #pragma unroll + for (int i = 0; i < 4; i++) { + #pragma unroll + for (int j = 0; j < 4; j++) { + reg_array[i * 4 + j] += reg_1[4 + i] * reg_2[4 + j]; + } + } + + __syncthreads(); + + float *C_buffer = &buffer[threadIdx.y * 3072 + 1024]; // [64, 32] + + #pragma unroll + for (int i = 0; i < 4; i++) { + #pragma unroll + for (int j = 0; j < 4; j++) { + C_buffer[(reg_1_idx * 4 + i) * 32 + reg_2_idx * 4 + j] = reg_array[i * 4 + j]; + } + } + __syncthreads(); + + #pragma unroll + for (int i = 0; i < 16; i++) { + atomicAdd(&dense_C_pt[dim_stride * 32 + i * 128 + thread_idx], C_buffer[i * 128 + thread_idx]); + } + __syncthreads(); + + } + +} + + +__global__ void reduce_sum_cuda_kernel( + float *sparse_A, // [batch_size, num_block, 32, 32] + int *indices, // [batch_size, num_block] + float *dense_C, // [batch_size, A_num_block, 32] + long batch_size, + long A_num_block, + long B_num_block, + long num_block +) { + + long batch_idx = blockIdx.y; + long block_idx = blockIdx.x * blockDim.y + threadIdx.y; + + long thread_idx = threadIdx.x; + + long batch_idx__block_idx = batch_idx * num_block + block_idx; + + long AB_block_idx = indices[batch_idx__block_idx]; + float *sparse_A_pt = &sparse_A[batch_idx__block_idx * 1024]; + + float reg_array[16]; + float value = 0; + + #pragma unroll + for (int i = 0; i < 8; i++) { + reg_array[i] = sparse_A_pt[i * 32 + thread_idx]; + } + #pragma unroll + for (int stride = 8; stride < 32; stride = stride + 8) { + #pragma unroll + for (int i = 0; i < 8; i++) { + reg_array[(stride + i) % 16] = sparse_A_pt[(stride + i) * 32 + thread_idx]; + } + #pragma unroll + for (int i = 0; i < 8; i++) { + value = value + reg_array[(stride - 8 + i) % 16]; + } + } + #pragma unroll + for (int i = 0; i < 8; i++) { + value = value + reg_array[8 + i]; + } + + float *dense_C_pt = &dense_C[(batch_idx * A_num_block + AB_block_idx / B_num_block) * 32]; + + atomicAdd(&dense_C_pt[thread_idx], value); + +} + +__global__ void scatter_cuda_kernel( + float *dense_A, // [batch_size, A_num_block, 32] + int *indices, // [batch_size, num_block] + float *sparse_C, // [batch_size, num_block, 32, 32] + long batch_size, + long A_num_block, + long B_num_block, + long num_block +) { + + long batch_idx = blockIdx.y; + long block_idx = blockIdx.x * blockDim.y + threadIdx.y; + + long thread_idx = threadIdx.x; + + long batch_idx__block_idx = batch_idx * num_block + block_idx; + + long AB_block_idx = indices[batch_idx__block_idx]; + float *dense_A_pt = &dense_A[(batch_idx * A_num_block + AB_block_idx / B_num_block) * 32]; + float *sparse_C_pt = &sparse_C[(batch_idx * num_block + block_idx) * 1024]; + + float value = dense_A_pt[thread_idx]; + + #pragma unroll + for (int i = 0; i < 32; i++) { + sparse_C_pt[i * 32 + thread_idx] = value; + } + +} diff --git a/venv/lib/python3.13/site-packages/transformers/kernels/mra/cuda_kernel.h b/venv/lib/python3.13/site-packages/transformers/kernels/mra/cuda_kernel.h new file mode 100644 index 0000000000000000000000000000000000000000..a95b46f7d159b11851143710034cf80c20aa6bf8 --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/kernels/mra/cuda_kernel.h @@ -0,0 +1,59 @@ + +#define WARP_SIZE 32 +#define FULL_MASK 0xffffffff +#define OPTIMAL_THREADS 256 + +__global__ void index_max_cuda_kernel( + float *index_vals, // [batch_size, 32, num_block] + int *indices, // [batch_size, num_block] + float *max_vals, // [batch_size, A_num_block * 32] + float *max_vals_scatter, // [batch_size, 32, num_block] + long batch_size, + long A_num_block, + long B_num_block, + long num_block +); + +__global__ void mm_to_sparse_cuda_kernel( + float *dense_A, // [batch_size, A_num_block, dim, 32] + float *dense_B, // [batch_size, B_num_block, dim, 32] + int *indices, // [batch_size, num_block] + float *sparse_C, // [batch_size, num_block, 32, 32] + long batch_size, + long A_num_block, + long B_num_block, + long dim, + long num_block +); + +__global__ void sparse_dense_mm_cuda_kernel( + float *sparse_A, // [batch_size, num_block, 32, 32] + int *indices, // [batch_size, num_block] + float *dense_B, // [batch_size, B_num_block, dim, 32] + float *dense_C, // [batch_size, A_num_block, dim, 32] + long batch_size, + long A_num_block, + long B_num_block, + long dim, + long num_block +); + +__global__ void reduce_sum_cuda_kernel( + float *sparse_A, // [batch_size, num_block, 32, 32] + int *indices, // [batch_size, num_block] + float *dense_C, // [batch_size, A_num_block, 32] + long batch_size, + long A_num_block, + long B_num_block, + long num_block +); + +__global__ void scatter_cuda_kernel( + float *dense_A, // [batch_size, A_num_block, 32] + int *indices, // [batch_size, num_block] + float *sparse_C, // [batch_size, num_block, 32, 32] + long batch_size, + long A_num_block, + long B_num_block, + long num_block +); diff --git a/venv/lib/python3.13/site-packages/transformers/kernels/mra/cuda_launch.cu b/venv/lib/python3.13/site-packages/transformers/kernels/mra/cuda_launch.cu new file mode 100644 index 0000000000000000000000000000000000000000..ba2a0cacfe614e75e06d2dde80dc77a6e8a4ec1a --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/kernels/mra/cuda_launch.cu @@ -0,0 +1,154 @@ +#include +#include +#include "cuda_launch.h" +#include "cuda_kernel.h" +#include + +////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////// + +std::vector index_max_kernel( + at::Tensor index_vals, // [batch_size, 32, num_block] + at::Tensor indices, // [batch_size, num_block], + int A_num_block, + int B_num_block +) { + int batch_size = indices.size(0); + int num_block = indices.size(1); + + at::Tensor max_vals = at::zeros({batch_size, A_num_block * 32}, index_vals.options()); + at::Tensor max_vals_scatter = at::zeros({batch_size, 32, num_block}, index_vals.options()); + + dim3 threads(256); + dim3 blocks(batch_size); + int shared_mem = A_num_block * 32 * sizeof(float); + + index_max_cuda_kernel<<>>( + index_vals.data_ptr(), + indices.data_ptr(), + max_vals.data_ptr(), + max_vals_scatter.data_ptr(), + batch_size, + A_num_block, + B_num_block, + num_block + ); + + return {max_vals, max_vals_scatter}; +} + +at::Tensor mm_to_sparse_kernel( + at::Tensor dense_A, // [batch_size, A_num_block, dim, 32] + at::Tensor dense_B, // [batch_size, B_num_block, dim, 32] + at::Tensor indices // [batch_size, num_block] +) { + int batch_size = dense_A.size(0); + int A_num_block = dense_A.size(1); + int B_num_block = dense_B.size(1); + int dim = dense_A.size(2); + int num_block = indices.size(1); + + at::Tensor sparse_C = at::zeros({batch_size, num_block, 32, 32}, dense_A.options()); + + dim3 threads(64, 4); + dim3 blocks(num_block / 4, batch_size); + + mm_to_sparse_cuda_kernel<<>>( + dense_A.data_ptr(), + dense_B.data_ptr(), + indices.data_ptr(), + sparse_C.data_ptr(), + batch_size, + A_num_block, + B_num_block, + dim, + num_block + ); + + return sparse_C; +} + +at::Tensor sparse_dense_mm_kernel( + at::Tensor sparse_A, // [batch_size, num_block, 32, 32] + at::Tensor indices, // [batch_size, num_block] + at::Tensor dense_B, // [batch_size, B_num_block, dim, 32] + int A_num_block +) { + int batch_size = sparse_A.size(0); + int num_block = sparse_A.size(1); + int B_num_block = dense_B.size(1); + int dim = dense_B.size(2); + + at::Tensor dense_C = at::zeros({batch_size, A_num_block, dim, 32}, dense_B.options()); + + dim3 threads(128, 2); + dim3 blocks(num_block / 2, batch_size); + + sparse_dense_mm_cuda_kernel<<>>( + sparse_A.data_ptr(), + indices.data_ptr(), + dense_B.data_ptr(), + dense_C.data_ptr(), + batch_size, + A_num_block, + B_num_block, + dim, + num_block + ); + + return dense_C; +} + +at::Tensor reduce_sum_kernel( + at::Tensor sparse_A, // [batch_size, num_block, 32, 32] + at::Tensor indices, // [batch_size, num_block] + int A_num_block, + int B_num_block +) { + int batch_size = sparse_A.size(0); + int num_block = sparse_A.size(1); + + at::Tensor dense_C = at::zeros({batch_size, A_num_block, 32}, sparse_A.options()); + + dim3 threads(32, 4); + dim3 blocks(num_block / 4, batch_size); + + reduce_sum_cuda_kernel<<>>( + sparse_A.data_ptr(), + indices.data_ptr(), + dense_C.data_ptr(), + batch_size, + A_num_block, + B_num_block, + num_block + ); + + return dense_C; +} + +at::Tensor scatter_kernel( + at::Tensor dense_A, // [batch_size, A_num_block, 32] + at::Tensor indices, // [batch_size, num_block] + int B_num_block +) { + int batch_size = dense_A.size(0); + int A_num_block = dense_A.size(1); + int num_block = indices.size(1); + + at::Tensor sparse_C = at::zeros({batch_size, num_block, 32, 32}, dense_A.options()); + + dim3 threads(32, 4); + dim3 blocks(num_block / 4, batch_size); + + scatter_cuda_kernel<<>>( + dense_A.data_ptr(), + indices.data_ptr(), + sparse_C.data_ptr(), + batch_size, + A_num_block, + B_num_block, + num_block + ); + + return sparse_C; +} diff --git a/venv/lib/python3.13/site-packages/transformers/kernels/mra/cuda_launch.h b/venv/lib/python3.13/site-packages/transformers/kernels/mra/cuda_launch.h new file mode 100644 index 0000000000000000000000000000000000000000..0200140ee337b8c5d9583767bbad1e842e9d4677 --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/kernels/mra/cuda_launch.h @@ -0,0 +1,39 @@ +#include +#include +#include + +#define min(a, b) ((a)<(b)?(a):(b)) +#define max(a, b) ((a)>(b)?(a):(b)) + +std::vector index_max_kernel( + at::Tensor index_vals, + at::Tensor indices, + int A_num_block, + int B_num_block +); + +at::Tensor mm_to_sparse_kernel( + at::Tensor dense_A, + at::Tensor dense_B, + at::Tensor indices +); + +at::Tensor sparse_dense_mm_kernel( + at::Tensor sparse_A, + at::Tensor indices, + at::Tensor dense_B, + int A_num_block +); + +at::Tensor reduce_sum_kernel( + at::Tensor sparse_A, + at::Tensor indices, + int A_num_block, + int B_num_block +); + +at::Tensor scatter_kernel( + at::Tensor dense_A, + at::Tensor indices, + int B_num_block +); diff --git a/venv/lib/python3.13/site-packages/transformers/kernels/mra/torch_extension.cpp b/venv/lib/python3.13/site-packages/transformers/kernels/mra/torch_extension.cpp new file mode 100644 index 0000000000000000000000000000000000000000..60c9262b779270a6e95ae54f53a67daa6d740a9e --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/kernels/mra/torch_extension.cpp @@ -0,0 +1,78 @@ +#include +#include +#include "cuda_launch.h" +#include + +std::vector index_max( + at::Tensor index_vals, + at::Tensor indices, + int A_num_block, + int B_num_block +) { + return index_max_kernel( + index_vals, + indices, + A_num_block, + B_num_block + ); +} + +at::Tensor mm_to_sparse( + at::Tensor dense_A, + at::Tensor dense_B, + at::Tensor indices +) { + return mm_to_sparse_kernel( + dense_A, + dense_B, + indices + ); +} + +at::Tensor sparse_dense_mm( + at::Tensor sparse_A, + at::Tensor indices, + at::Tensor dense_B, + int A_num_block +) { + return sparse_dense_mm_kernel( + sparse_A, + indices, + dense_B, + A_num_block + ); +} + +at::Tensor reduce_sum( + at::Tensor sparse_A, + at::Tensor indices, + int A_num_block, + int B_num_block +) { + return reduce_sum_kernel( + sparse_A, + indices, + A_num_block, + B_num_block + ); +} + +at::Tensor scatter( + at::Tensor dense_A, + at::Tensor indices, + int B_num_block +) { + return scatter_kernel( + dense_A, + indices, + B_num_block + ); +} + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("index_max", &index_max, "index_max (CUDA)"); + m.def("mm_to_sparse", &mm_to_sparse, "mm_to_sparse (CUDA)"); + m.def("sparse_dense_mm", &sparse_dense_mm, "sparse_dense_mm (CUDA)"); + m.def("reduce_sum", &reduce_sum, "reduce_sum (CUDA)"); + m.def("scatter", &scatter, "scatter (CUDA)"); +} diff --git a/venv/lib/python3.13/site-packages/transformers/kernels/rwkv/wkv_cuda.cu b/venv/lib/python3.13/site-packages/transformers/kernels/rwkv/wkv_cuda.cu new file mode 100644 index 0000000000000000000000000000000000000000..571d5a8a8307e95aac689eb3c9333d1ad350c7de --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/kernels/rwkv/wkv_cuda.cu @@ -0,0 +1,187 @@ +#include +#include + +#define MIN_VALUE (-1e38) + +template +__global__ void kernel_forward( + const int B, const int T, const int C, const F *__restrict__ const _w, const F *__restrict__ const _u, + const F *__restrict__ const _k, const F *__restrict__ const _v, F *__restrict__ const _y +) { + const int idx = blockIdx.x * blockDim.x + threadIdx.x; + const int _b = idx / C; + const int _c = idx % C; + const int _offset = _b * T * C + _c; + + F u = _u[_c]; + F w = _w[_c]; + const F *__restrict__ const k = _k + _offset; + const F *__restrict__ const v = _v + _offset; + F *__restrict__ const y = _y + _offset; + + // aa and bb are running sums divided by exp(pp) (to avoid overflow) + F aa = 0, bb = 0, pp = MIN_VALUE; + for (int i = 0; i < T; i++) { + const int ii = i * C; + const F kk = k[ii]; + const F vv = v[ii]; + + F ww = u + kk; + F p = max(pp, ww); + F e1 = exp(pp - p); + F e2 = exp(ww - p); + y[ii] = (e1 * aa + e2 * vv) / (e1 * bb + e2); + + ww = w + pp; + p = max(ww, kk); + e1 = exp(ww - p); + e2 = exp(kk - p); + aa = e1 * aa + e2 * vv; + bb = e1 * bb + e2; + pp = p; + } +} + +template +__global__ void kernel_forward_with_state( + const int B, const int T, const int C, const F *__restrict__ const _w, const F *__restrict__ const _u, + const F *__restrict__ const _k, const F *__restrict__ const _v, F *__restrict__ const _y, F *__restrict__ const _s +) { + const int idx = blockIdx.x * blockDim.x + threadIdx.x; + const int _b = idx / C; + const int _c = idx % C; + const int _offset_s = _b * C * 3 + _c * 3; + const int _offset = _b * T * C + _c; + + F u = _u[_c]; + F w = _w[_c]; + const F *__restrict__ const k = _k + _offset; + const F *__restrict__ const v = _v + _offset; + F *__restrict__ const y = _y + _offset; + F *__restrict__ const s = _s + _offset_s; + + // aa and bb are running sums divided by exp(pp) (to avoid overflow) + F aa = s[0], bb = s[1], pp = s[2]; + for (int i = 0; i < T; i++) { + const int ii = i * C; + const F kk = k[ii]; + const F vv = v[ii]; + + F ww = u + kk; + F p = max(pp, ww); + F e1 = exp(pp - p); + F e2 = exp(ww - p); + y[ii] = (e1 * aa + e2 * vv) / (e1 * bb + e2); + + ww = w + pp; + p = max(ww, kk); + e1 = exp(ww - p); + e2 = exp(kk - p); + aa = e1 * aa + e2 * vv; + bb = e1 * bb + e2; + pp = p; + } + s[0] = aa; + s[1] = bb; + s[2] = pp; +} + +template +__global__ void kernel_backward( + const int B, const int T, const int C, const F *__restrict__ const _w, const F *__restrict__ const _u, + const F *__restrict__ const _k, const F *__restrict__ const _v, const F *__restrict__ const _y, + const F *__restrict__ const _gy, F *__restrict__ const _gw, F *__restrict__ const _gu, F *__restrict__ const _gk, + F *__restrict__ const _gv +) { + const int idx = blockIdx.x * blockDim.x + threadIdx.x; + const int _b = idx / C; + const int _c = idx % C; + const int _offset = _b * T * C + _c; + + F u = _u[_c]; + F w = _w[_c]; + const F *__restrict__ const k = _k + _offset; + const F *__restrict__ const v = _v + _offset; + const F *__restrict__ const y = _y + _offset; + const F *__restrict__ const gy = _gy + _offset; + F *__restrict__ const gk = _gk + _offset; + F *__restrict__ const gv = _gv + _offset; + + F q[Tmax], r[Tmax]; + + F gw = 0, gu = 0, aa = 0, bb = 0, ga = 0, gb = 0, pp = MIN_VALUE; + for (int i = 0; i < T; i++) { + const int ii = i * C; + const F kk = k[ii]; + const F vv = v[ii]; + const F yy = y[ii]; + + F ww = u + kk; + F p = max(pp, ww); + F e1 = exp(pp - p); + F e2 = exp(ww - p); + const F qq = gy[ii] / (e1 * bb + e2); + gw += (ga - gb * yy) * e1 * qq; + gu += (vv - yy) * e2 * qq; + q[i] = qq; + r[i] = ww - p; + + ww = w + pp; + p = max(ww, kk); + e1 = exp(ww - p); + e2 = exp(kk - p); + ga = e1 * (aa + ga); + gb = e1 * (bb + gb); + aa = e1 * aa + e2 * vv; + bb = e1 * bb + e2; + pp = p; + } + const int _offsetBC = _b * C + _c; + _gw[_offsetBC] = gw * _w[_c]; // multiply by w because of w -> -exp(w) in python forward() + _gu[_offsetBC] = gu; + + aa = 0, bb = 0, pp = MIN_VALUE; + for (int i = T - 1; i >= 0; i--) { + const int ii = i * C; + const F kk = k[ii]; + const F vv = v[ii]; + const F yy = y[ii]; + const F qq = q[i]; + const F rr = r[i]; + + F e1 = qq * exp(rr); + F e2 = exp(kk + pp); + gk[ii] = e1 * (vv - yy) + e2 * (aa * vv + bb); + gv[ii] = e1 + e2 * aa; + + const F ww = w + pp; + const F www = rr - u - kk; + const F p = max(ww, www); + e1 = exp(ww - p); + e2 = qq * exp(www - p); + aa = e1 * aa + e2; + bb = e1 * bb - e2 * yy; + pp = p; + } +} + +void cuda_forward(int B, int T, int C, float *w, float *u, float *k, float *v, float *y) { + dim3 threadsPerBlock( min(C, 32) ); // requires --maxrregcount 60 for optimal performance + assert(B * C % threadsPerBlock.x == 0); + dim3 numBlocks(B * C / threadsPerBlock.x); + kernel_forward<<>>(B, T, C, w, u, k, v, y); +} + +void cuda_forward_with_state(int B, int T, int C, float *w, float *u, float *k, float *v, float *y, float *s) { + dim3 threadsPerBlock( min(C, 32) ); // requires --maxrregcount 60 for optimal performance + assert(B * C % threadsPerBlock.x == 0); + dim3 numBlocks(B * C / threadsPerBlock.x); + kernel_forward_with_state<<>>(B, T, C, w, u, k, v, y, s); +} + +void cuda_backward(int B, int T, int C, float *w, float *u, float *k, float *v, float *y, float *gy, float *gw, float *gu, float *gk, float *gv) { + dim3 threadsPerBlock( min(C, 32) ); // requires --maxrregcount 60 for optimal performance + assert(B * C % threadsPerBlock.x == 0); + dim3 numBlocks(B * C / threadsPerBlock.x); + kernel_backward<<>>(B, T, C, w, u, k, v, y, gy, gw, gu, gk, gv); +} diff --git a/venv/lib/python3.13/site-packages/transformers/kernels/rwkv/wkv_cuda_bf16.cu b/venv/lib/python3.13/site-packages/transformers/kernels/rwkv/wkv_cuda_bf16.cu new file mode 100644 index 0000000000000000000000000000000000000000..042cb4aba1db98be5916aea1de86a7fed0b6510d --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/kernels/rwkv/wkv_cuda_bf16.cu @@ -0,0 +1,186 @@ +#include +#include +#include "ATen/ATen.h" +#define MIN_VALUE (-1e38) +typedef at::BFloat16 bf16; + +__global__ void kernel_forward_bf16( + const int B, const int T, const int C, const float *__restrict__ const _w, const bf16 *__restrict__ const _u, + const bf16 *__restrict__ const _k, const bf16 *__restrict__ const _v, bf16 *__restrict__ const _y +) { + const int idx = blockIdx.x * blockDim.x + threadIdx.x; + const int _b = idx / C; + const int _c = idx % C; + const int _offset = _b * T * C + _c; + + float u = float(_u[_c]); + float w = _w[_c]; + const bf16 *__restrict__ const k = _k + _offset; + const bf16 *__restrict__ const v = _v + _offset; + bf16 *__restrict__ const y = _y + _offset; + + // aa and bb are running sums divided by exp(pp) (to avoid overflow) + float aa = 0, bb = 0, pp = MIN_VALUE; + for (int i = 0; i < T; i++) { + const int ii = i * C; + const float kk = float(k[ii]); + const float vv = float(v[ii]); + + float ww = u + kk; + float p = max(pp, ww); + float e1 = exp(pp - p); + float e2 = exp(ww - p); + y[ii] = bf16((e1 * aa + e2 * vv) / (e1 * bb + e2)); + + ww = w + pp; + p = max(ww, kk); + e1 = exp(ww - p); + e2 = exp(kk - p); + aa = e1 * aa + e2 * vv; + bb = e1 * bb + e2; + pp = p; + } +} + +__global__ void kernel_forward_with_state_bf16( + const int B, const int T, const int C, const float *__restrict__ const _w, const bf16 *__restrict__ const _u, + const bf16 *__restrict__ const _k, const bf16 *__restrict__ const _v, bf16 *__restrict__ const _y, + float *__restrict__ const _s +) { + const int idx = blockIdx.x * blockDim.x + threadIdx.x; + const int _b = idx / C; + const int _c = idx % C; + const int _offset_s = _b * C * 3 + _c * 3; + const int _offset = _b * T * C + _c; + + float u = float(_u[_c]); + float w = _w[_c]; + const bf16 *__restrict__ const k = _k + _offset; + const bf16 *__restrict__ const v = _v + _offset; + bf16 *__restrict__ const y = _y + _offset; + float *__restrict__ const s = _s + _offset_s; + + // aa and bb are running sums divided by exp(pp) (to avoid overflow) + float aa = s[0], bb = s[1], pp = s[2]; + for (int i = 0; i < T; i++) { + const int ii = i * C; + const float kk = float(k[ii]); + const float vv = float(v[ii]); + + float ww = u + kk; + float p = max(pp, ww); + float e1 = exp(pp - p); + float e2 = exp(ww - p); + y[ii] = bf16(e1 * aa + e2 * vv) / (e1 * bb + e2); + + ww = w + pp; + p = max(ww, kk); + e1 = exp(ww - p); + e2 = exp(kk - p); + aa = e1 * aa + e2 * vv; + bb = e1 * bb + e2; + pp = p; + } + s[0] = aa; + s[1] = bb; + s[2] = pp; +} + +__global__ void kernel_backward_bf16( + const int B, const int T, const int C, const float *__restrict__ const _w, const bf16 *__restrict__ const _u, + const bf16 *__restrict__ const _k, const bf16 *__restrict__ const _v, const bf16 *__restrict__ const _y, + const bf16 *__restrict__ const _gy, bf16 *__restrict__ const _gw, bf16 *__restrict__ const _gu, + bf16 *__restrict__ const _gk, bf16 *__restrict__ const _gv +) { + const int idx = blockIdx.x * blockDim.x + threadIdx.x; + const int _b = idx / C; + const int _c = idx % C; + const int _offset = _b * T * C + _c; + + float u = float(_u[_c]); + float w = _w[_c]; + const bf16 *__restrict__ const k = _k + _offset; + const bf16 *__restrict__ const v = _v + _offset; + const bf16 *__restrict__ const y = _y + _offset; + const bf16 *__restrict__ const gy = _gy + _offset; + bf16 *__restrict__ const gk = _gk + _offset; + bf16 *__restrict__ const gv = _gv + _offset; + + float q[Tmax], r[Tmax]; + + float gw = 0, gu = 0, aa = 0, bb = 0, ga = 0, gb = 0, pp = MIN_VALUE; + for (int i = 0; i < T; i++) { + const int ii = i * C; + const float kk = float(k[ii]); + const float vv = float(v[ii]); + const float yy = float(y[ii]); + + float ww = u + kk; + float p = max(pp, ww); + float e1 = exp(pp - p); + float e2 = exp(ww - p); + const float qq = float(gy[ii]) / (e1 * bb + e2); + gw += (ga - gb * yy) * e1 * qq; + gu += (vv - yy) * e2 * qq; + q[i] = qq; + r[i] = ww - p; + + ww = w + pp; + p = max(ww, kk); + e1 = exp(ww - p); + e2 = exp(kk - p); + ga = e1 * (aa + ga); + gb = e1 * (bb + gb); + aa = e1 * aa + e2 * vv; + bb = e1 * bb + e2; + pp = p; + } + const int _offsetBC = _b * C + _c; + _gw[_offsetBC] = bf16(gw * _w[_c]); // multiply by w because of w -> -exp(w) in python forward() + _gu[_offsetBC] = bf16(gu); + + aa = 0, bb = 0, pp = MIN_VALUE; + for (int i = T - 1; i >= 0; i--) { + const int ii = i * C; + const float kk = float(k[ii]); + const float vv = float(v[ii]); + const float yy = float(y[ii]); + const float qq = q[i]; + const float rr = r[i]; + + float e1 = qq * exp(rr); + float e2 = exp(kk + pp); + gk[ii] = bf16(e1 * (vv - yy) + e2 * (aa * vv + bb)); + gv[ii] = bf16(e1 + e2 * aa); + + const float ww = w + pp; + const float www = rr - u - kk; + const float p = max(ww, www); + e1 = exp(ww - p); + e2 = qq * exp(www - p); + aa = e1 * aa + e2; + bb = e1 * bb - e2 * yy; + pp = p; + } +} + +void cuda_forward_bf16(int B, int T, int C, float *w, bf16 *u, bf16 *k, bf16 *v, bf16 *y) { + dim3 threadsPerBlock( min(C, 32) ); // requires --maxrregcount 60 for optimal performance + assert(B * C % threadsPerBlock.x == 0); + dim3 numBlocks(B * C / threadsPerBlock.x); + kernel_forward_bf16<<>>(B, T, C, w, u, k, v, y); +} + +void cuda_forward_with_state_bf16(int B, int T, int C, float *w, bf16 *u, bf16 *k, bf16 *v, bf16 *y, float *s) { + dim3 threadsPerBlock( min(C, 32) ); // requires --maxrregcount 60 for optimal performance + assert(B * C % threadsPerBlock.x == 0); + dim3 numBlocks(B * C / threadsPerBlock.x); + kernel_forward_with_state_bf16<<>>(B, T, C, w, u, k, v, y, s); +} + +void cuda_backward_bf16(int B, int T, int C, float *w, bf16 *u, bf16 *k, bf16 *v, bf16 *y, bf16 *gy, bf16 *gw, bf16 *gu, bf16 *gk, bf16 *gv) { + dim3 threadsPerBlock( min(C, 32) ); // requires --maxrregcount 60 for optimal performance + assert(B * C % threadsPerBlock.x == 0); + dim3 numBlocks(B * C / threadsPerBlock.x); + kernel_backward_bf16<<>>(B, T, C, w, u, k, v, y, gy, gw, gu, gk, gv); +} diff --git a/venv/lib/python3.13/site-packages/transformers/kernels/rwkv/wkv_op.cpp b/venv/lib/python3.13/site-packages/transformers/kernels/rwkv/wkv_op.cpp new file mode 100644 index 0000000000000000000000000000000000000000..55e7280665927b523a88021d5111daf28a63c905 --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/kernels/rwkv/wkv_op.cpp @@ -0,0 +1,66 @@ +#include +#include "ATen/ATen.h" +typedef at::BFloat16 bf16; + +void cuda_forward(int B, int T, int C, float *w, float *u, float *k, float *v, float *y); +void cuda_forward_bf16(int B, int T, int C, float *w, bf16 *u, bf16 *k, bf16 *v, bf16 *y); +void cuda_forward_with_state(int B, int T, int C, float *w, float *u, float *k, float *v, float *y, float *s); +void cuda_forward_with_state_bf16(int B, int T, int C, float *w, bf16 *u, bf16 *k, bf16 *v, bf16 *y, float *s); +void cuda_backward(int B, int T, int C, float *w, float *u, float *k, float *v, float *y, float *gy, float *gw, float *gu, float *gk, float *gv); +void cuda_backward_bf16(int B, int T, int C, float *w, bf16 *u, bf16 *k, bf16 *v, bf16 *y, bf16 *gy, bf16 *gw, bf16 *gu, bf16 *gk, bf16 *gv); + +void forward(torch::Tensor &w, torch::Tensor &u, torch::Tensor &k, torch::Tensor &v, torch::Tensor &y) { + const int B = k.size(0); + const int T = k.size(1); + const int C = k.size(2); + cuda_forward(B, T, C, w.data_ptr(), u.data_ptr(), k.data_ptr(), v.data_ptr(), y.data_ptr()); +} +void forward_bf16(torch::Tensor &w, torch::Tensor &u, torch::Tensor &k, torch::Tensor &v, torch::Tensor &y) { + const int B = k.size(0); + const int T = k.size(1); + const int C = k.size(2); + cuda_forward_bf16(B, T, C, w.data_ptr(), u.data_ptr(), k.data_ptr(), v.data_ptr(), y.data_ptr()); +} +void forward_with_state(torch::Tensor &w, torch::Tensor &u, torch::Tensor &k, torch::Tensor &v, torch::Tensor &y, torch::Tensor &s) { + const int B = k.size(0); + const int T = k.size(1); + const int C = k.size(2); + cuda_forward_with_state(B, T, C, w.data_ptr(), u.data_ptr(), k.data_ptr(), v.data_ptr(), y.data_ptr(), s.data_ptr()); +} +void forward_with_state_bf16(torch::Tensor &w, torch::Tensor &u, torch::Tensor &k, torch::Tensor &v, torch::Tensor &y, torch::Tensor &s) { + const int B = k.size(0); + const int T = k.size(1); + const int C = k.size(2); + cuda_forward_with_state_bf16(B, T, C, w.data_ptr(), u.data_ptr(), k.data_ptr(), v.data_ptr(), y.data_ptr(), s.data_ptr()); +} +void backward(torch::Tensor &w, torch::Tensor &u, torch::Tensor &k, torch::Tensor &v, torch::Tensor &y, torch::Tensor &gy, torch::Tensor &gw, torch::Tensor &gu, torch::Tensor &gk, torch::Tensor &gv) { + const int B = k.size(0); + const int T = k.size(1); + const int C = k.size(2); + cuda_backward(B, T, C, w.data_ptr(), u.data_ptr(), k.data_ptr(), v.data_ptr(), y.data_ptr(), gy.data_ptr(), gw.data_ptr(), gu.data_ptr(), gk.data_ptr(), gv.data_ptr()); +} +void backward_bf16(torch::Tensor &w, torch::Tensor &u, torch::Tensor &k, torch::Tensor &v, torch::Tensor &y, torch::Tensor &gy, torch::Tensor &gw, torch::Tensor &gu, torch::Tensor &gk, torch::Tensor &gv) { + const int B = k.size(0); + const int T = k.size(1); + const int C = k.size(2); + cuda_backward_bf16(B, T, C, w.data_ptr(), u.data_ptr(), k.data_ptr(), v.data_ptr(), y.data_ptr(), + gy.data_ptr(), gw.data_ptr(), gu.data_ptr(), gk.data_ptr(), gv.data_ptr()); +} + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("forward", &forward, "wkv forward"); + m.def("forward_bf16", &forward_bf16, "wkv forward bf16"); + m.def("forward_with_state", &forward_with_state, "wkv forward with state"); + m.def("forward_with_state_bf16", &forward_with_state_bf16, "wkv forward with state bf16"); + m.def("backward", &backward, "wkv backward"); + m.def("backward_bf16", &backward_bf16, "wkv backward bf16"); +} + +TORCH_LIBRARY(wkv, m) { + m.def("forward", forward); + m.def("forward_bf16", forward_bf16); + m.def("forward_with_state", forward_with_state); + m.def("forward_with_state_bf16", forward_with_state_bf16); + m.def("backward", backward); + m.def("backward_bf16", backward_bf16); +} diff --git a/venv/lib/python3.13/site-packages/transformers/kernels/yoso/common.h b/venv/lib/python3.13/site-packages/transformers/kernels/yoso/common.h new file mode 100644 index 0000000000000000000000000000000000000000..e5085c88dd3ea9a12eec264a8c48946bf2b80b23 --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/kernels/yoso/common.h @@ -0,0 +1,10 @@ + +#define min(a, b) ((a)<(b)?(a):(b)) +#define max(a, b) ((a)>(b)?(a):(b)) +#define ceil_divide(a, b) ((a)/(b)+((a)%(b)!=0)) +#define select(cond, a, b) ((cond)?(a):(b)) +#define PI 3.141592 +#define EPSILON 1e-8 +#define MAX_VAL 1e12 +#define MIN_VAL -1e12 +#define EMPTY_VALUE -1 diff --git a/venv/lib/python3.13/site-packages/transformers/kernels/yoso/common_cuda.h b/venv/lib/python3.13/site-packages/transformers/kernels/yoso/common_cuda.h new file mode 100644 index 0000000000000000000000000000000000000000..97030870649a2fdac58cb26cf966e8f5c8cc7909 --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/kernels/yoso/common_cuda.h @@ -0,0 +1,9 @@ + +#define MAX_THREADS_PER_BLOCK 1024 +#define OPTIMAL_THREADS_PER_BLOCK 256 +#define WARP_SIZE 32 +#define MAX_NUM_BLOCK_X 2147483647 +#define MAX_NUM_BLOCK_Y 65535 +#define MAX_NUM_BLOCK_Z 65535 +#define MAX_SHARED_MEM_PER_BLOCK 48000 +#define FULL_MASK 0xffffffff diff --git a/venv/lib/python3.13/site-packages/transformers/kernels/yoso/common_cuda_device.h b/venv/lib/python3.13/site-packages/transformers/kernels/yoso/common_cuda_device.h new file mode 100644 index 0000000000000000000000000000000000000000..6674f93afdc25ab35c5d83881d00028bcf2989fc --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/kernels/yoso/common_cuda_device.h @@ -0,0 +1,79 @@ + +#include "common.h" + +template +__device__ int set_insert(T *set, int set_size, T value) { + int slot = value % set_size; + int start_slot = slot; + while (true) { + T prev = atomicCAS(&set[slot], EMPTY_VALUE, value); + if (prev == EMPTY_VALUE || prev == value) { + return slot; + } + slot = (slot + 1) % set_size; + if (slot == start_slot) { + return -1; + } + } + return -1; +} + +template +__device__ int set_lookup(T *set, int set_size, T value) { + int slot = value % set_size; + int start_slot = slot; + while (true) { + if (set[slot] == value) { + return slot; + } + slot = (slot + 1) % set_size; + if (slot == start_slot) { + return -1; + } + } + return -1; +} + +template +__device__ void init_buffer(T init_value, T *buffer, int buffer_size, int num_threads, int thread_id) { + __syncthreads(); + for (int i = 0; i < buffer_size; i = i + num_threads) { + int offset_idx = i + thread_id; + if (offset_idx < buffer_size) { + buffer[offset_idx] = init_value; + } + } + __syncthreads(); +} + +template +__device__ void copy_data(T *src_pt, T *dist_pt, int data_length, int num_threads, int thread_id) { + __syncthreads(); + for (int i = 0; i < data_length; i = i + num_threads) { + int offset_idx = i + thread_id; + if (offset_idx < data_length) { + dist_pt[offset_idx] = src_pt[offset_idx]; + } + } + __syncthreads(); +} + +template +__device__ void init_buffer_nonblocking(T init_value, T *buffer, int buffer_size, int num_threads, int thread_id) { + for (int i = 0; i < buffer_size; i = i + num_threads) { + int offset_idx = i + thread_id; + if (offset_idx < buffer_size) { + buffer[offset_idx] = init_value; + } + } +} + +template +__device__ void copy_data_nonblocking(T *src_pt, T *dist_pt, int data_length, int num_threads, int thread_id) { + for (int i = 0; i < data_length; i = i + num_threads) { + int offset_idx = i + thread_id; + if (offset_idx < data_length) { + dist_pt[offset_idx] = src_pt[offset_idx]; + } + } +} diff --git a/venv/lib/python3.13/site-packages/transformers/kernels/yoso/fast_lsh_cumulation.cu b/venv/lib/python3.13/site-packages/transformers/kernels/yoso/fast_lsh_cumulation.cu new file mode 100644 index 0000000000000000000000000000000000000000..c6b13e6cb5f53c9c62e51d2c399a14d14dab7037 --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/kernels/yoso/fast_lsh_cumulation.cu @@ -0,0 +1,588 @@ +// File from https://github.com/mlpen/YOSO/blob/main/encoders/backbones/efficient_attentions/yoso/yoso_v1/cuda/fast_lsh_cumulation.cu + +#include +#include +#include "fast_lsh_cumulation.h" +#include "fast_lsh_cumulation_cuda.h" +#include "common_cuda.h" +#include "common.h" +#include +////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////// + +std::vector fast_hash_ver1_kernel( + at::Tensor query_mask, + at::Tensor query_vector, + at::Tensor key_mask, + at::Tensor key_vector, + int num_hash_f, + int hash_code_len, + bool use_cuda +) { + + int batch_size = query_vector.size(0); + int num_query = query_vector.size(1); + int num_key = key_vector.size(1); + int vector_dim = query_vector.size(2); + + int num_hash_per_part = vector_dim / hash_code_len; + int num_part = max(1, ceil_divide(num_hash_f, num_hash_per_part)); + + at::Tensor Dmat = 2 * at::randint(0, 2, {batch_size, 3, num_part, vector_dim}, query_mask.options()) - 1; + at::Tensor query_hash_code = at::zeros({batch_size, num_query, num_hash_f}, query_mask.options()); + at::Tensor key_hash_code = at::zeros({batch_size, num_key, num_hash_f}, key_mask.options()); + + int *query_mask_ptr = query_mask.data_ptr(); + float *query_vector_ptr = query_vector.data_ptr(); + int *key_mask_ptr = key_mask.data_ptr(); + float *key_vector_ptr = key_vector.data_ptr(); + + int *Dmat_ptr = Dmat.data_ptr(); + + int *query_hash_code_ptr = query_hash_code.data_ptr(); + int *key_hash_code_ptr = key_hash_code.data_ptr(); + + if (use_cuda) { + { + dim3 threads(vector_dim); + dim3 blocks(num_part, num_query, batch_size); + int shared_mem = vector_dim * sizeof(float); + fast_hash_ver1_cuda_kernel<<>>( + query_mask_ptr, + query_vector_ptr, + Dmat_ptr, + query_hash_code_ptr, + batch_size, + num_query, + vector_dim, + num_part, + num_hash_f, + hash_code_len + ); + } + { + dim3 threads(vector_dim); + dim3 blocks(num_part, num_key, batch_size); + int shared_mem = vector_dim * sizeof(float); + fast_hash_ver1_cuda_kernel<<>>( + key_mask_ptr, + key_vector_ptr, + Dmat_ptr, + key_hash_code_ptr, + batch_size, + num_key, + vector_dim, + num_part, + num_hash_f, + hash_code_len + ); + } + } + + return {query_hash_code, key_hash_code}; + +} + +at::Tensor lsh_cumulation_ver1_kernel( + at::Tensor query_mask, + at::Tensor query_hash_code, + at::Tensor key_mask, + at::Tensor key_hash_code, + at::Tensor value, + int hashtable_capacity, + bool use_cuda +) { + + int batch_size = query_hash_code.size(0); + int num_hash_f = query_hash_code.size(2); + + int num_query = query_hash_code.size(1); + int num_key = key_hash_code.size(1); + int value_dim = value.size(2); + + at::Tensor hashtable_value = at::empty({batch_size, num_hash_f, hashtable_capacity, WARP_SIZE}, value.options()); + at::Tensor cumulation_value = at::zeros({batch_size, num_query, value_dim}, value.options()); + + if (use_cuda) { + int threads_x = WARP_SIZE; + int threads_y = OPTIMAL_THREADS_PER_BLOCK / WARP_SIZE; + int block_x_step1 = num_key / threads_y; + int block_x_step2 = num_query / threads_y; + int block_y = batch_size; + + dim3 threads(threads_x, threads_y); + dim3 blocks_step1(block_x_step1, block_y); + dim3 blocks_step2(block_x_step2, block_y); + + int *query_mask_ptr = query_mask.data_ptr(); + int *query_hash_code_ptr = query_hash_code.data_ptr(); + int *key_mask_ptr = key_mask.data_ptr(); + int *key_hash_code_ptr = key_hash_code.data_ptr(); + float *value_ptr = value.data_ptr(); + float *hashtable_value_ptr = hashtable_value.data_ptr(); + float *cumulation_value_ptr = cumulation_value.data_ptr(); + + for (int value_offset = 0; value_offset < value_dim; value_offset = value_offset + WARP_SIZE) { + + cudaMemset(hashtable_value_ptr, 0, (batch_size * num_hash_f * hashtable_capacity * WARP_SIZE) * sizeof(float)); + + lsh_cumulation_ver1_step1_cuda_kernel<<>>( + key_mask_ptr, + key_hash_code_ptr, + value_ptr, + hashtable_value_ptr, + batch_size, + num_hash_f, + hashtable_capacity, + num_key, + value_dim, + value_offset + ); + + lsh_cumulation_ver1_step2_cuda_kernel<<>>( + query_mask_ptr, + query_hash_code_ptr, + hashtable_value_ptr, + cumulation_value_ptr, + batch_size, + num_hash_f, + hashtable_capacity, + num_query, + value_dim, + value_offset + ); + } + + } + + return cumulation_value; + +} + +at::Tensor lsh_weighted_cumulation_ver1_kernel( + at::Tensor query_mask, + at::Tensor query_hash_code, + at::Tensor query_weight, + at::Tensor key_mask, + at::Tensor key_hash_code, + at::Tensor key_weight, + at::Tensor value, + int hashtable_capacity, + bool use_cuda +) { + + int batch_size = query_hash_code.size(0); + int num_hash_f = query_hash_code.size(2); + + int num_query = query_hash_code.size(1); + int num_key = key_hash_code.size(1); + int value_dim = value.size(2); + int weight_dim = query_weight.size(2); + + at::Tensor hashtable_value = at::zeros({batch_size, num_hash_f, hashtable_capacity, WARP_SIZE}, value.options()); + at::Tensor cumulation_value = at::zeros({batch_size, num_query, value_dim}, value.options()); + + if (use_cuda) { + int threads_x = WARP_SIZE; + int threads_y = OPTIMAL_THREADS_PER_BLOCK / WARP_SIZE; + int block_x_step1 = num_key / threads_y; + int block_x_step2 = num_query / threads_y; + int block_y = batch_size; + + dim3 threads(threads_x, threads_y); + dim3 blocks_step1(block_x_step1, block_y); + dim3 blocks_step2(block_x_step2, block_y); + + int *query_mask_ptr = query_mask.data_ptr(); + int *query_hash_code_ptr = query_hash_code.data_ptr(); + float *query_weight_ptr = query_weight.data_ptr(); + int *key_mask_ptr = key_mask.data_ptr(); + int *key_hash_code_ptr = key_hash_code.data_ptr(); + float *key_weight_ptr = key_weight.data_ptr(); + float *value_ptr = value.data_ptr(); + float *hashtable_value_ptr = hashtable_value.data_ptr(); + float *cumulation_value_ptr = cumulation_value.data_ptr(); + + for (int value_offset = 0; value_offset < value_dim; value_offset = value_offset + WARP_SIZE) { + for (int weight_idx = 0; weight_idx < weight_dim; weight_idx++) { + + cudaMemset(hashtable_value_ptr, 0, (batch_size * num_hash_f * hashtable_capacity * WARP_SIZE) * sizeof(float)); + + lsh_weighted_cumulation_ver1_step1_cuda_kernel<<>>( + key_mask_ptr, + key_hash_code_ptr, + key_weight_ptr, + value_ptr, + hashtable_value_ptr, + batch_size, + num_hash_f, + hashtable_capacity, + num_key, + value_dim, + weight_dim, + value_offset, + weight_idx + ); + + lsh_weighted_cumulation_ver1_step2_cuda_kernel<<>>( + query_mask_ptr, + query_hash_code_ptr, + query_weight_ptr, + hashtable_value_ptr, + cumulation_value_ptr, + batch_size, + num_hash_f, + hashtable_capacity, + num_query, + value_dim, + weight_dim, + value_offset, + weight_idx + ); + } + } + + } + + return cumulation_value; + +} + +at::Tensor lsh_weighted_cumulation_ver2_kernel( + at::Tensor query_mask, + at::Tensor query_hash_code, + at::Tensor query_weight, + at::Tensor key_mask, + at::Tensor key_hash_code, + at::Tensor key_weight, + at::Tensor value, + int hashtable_capacity, + bool use_cuda +) { + + int batch_size = query_hash_code.size(0); + int num_hash_f = query_hash_code.size(2); + + int num_query = query_hash_code.size(1); + int num_key = key_hash_code.size(1); + int value_dim = value.size(2); + int weight_dim = query_weight.size(2); + + at::Tensor count_sort_table = at::zeros({batch_size, num_hash_f, hashtable_capacity}, query_hash_code.options()); + at::Tensor key_sorted_idxes = at::zeros({batch_size, num_hash_f, num_key}, query_hash_code.options()); + at::Tensor query_info = at::zeros({batch_size, num_query, 2, num_hash_f}, query_hash_code.options()); + at::Tensor cumulation_value = at::zeros({batch_size, num_query, value_dim}, value.options()); + + if (use_cuda) { + + int *query_mask_ptr = query_mask.data_ptr(); + int *query_hash_code_ptr = query_hash_code.data_ptr(); + float *query_weight_ptr = query_weight.data_ptr(); + int *key_mask_ptr = key_mask.data_ptr(); + int *key_hash_code_ptr = key_hash_code.data_ptr(); + float *key_weight_ptr = key_weight.data_ptr(); + float *value_ptr = value.data_ptr(); + + int *count_sort_table_ptr = count_sort_table.data_ptr(); + int *key_sorted_idxes_ptr = key_sorted_idxes.data_ptr(); + int *query_info_ptr = query_info.data_ptr(); + + float *cumulation_value_ptr = cumulation_value.data_ptr(); + + { + dim3 threads_step13(num_hash_f, max(1, OPTIMAL_THREADS_PER_BLOCK / num_hash_f)); + dim3 blocks_step13(num_key / max(1, OPTIMAL_THREADS_PER_BLOCK / num_hash_f), batch_size); + dim3 threads_step2(min(hashtable_capacity, OPTIMAL_THREADS_PER_BLOCK)); + dim3 blocks_step2(num_hash_f, batch_size); + int shared_mem = hashtable_capacity * sizeof(float); + count_sort_step1_cuda_kernel<<>>( + key_mask_ptr, + key_hash_code_ptr, + count_sort_table_ptr, + batch_size, + num_hash_f, + hashtable_capacity, + num_key + ); + count_sort_step2_cuda_kernel<<>>( + count_sort_table_ptr, + batch_size, + num_hash_f, + hashtable_capacity + ); + count_sort_step3_cuda_kernel<<>>( + key_mask_ptr, + key_hash_code_ptr, + count_sort_table_ptr, + key_sorted_idxes_ptr, + batch_size, + num_hash_f, + hashtable_capacity, + num_key + ); + } + { + dim3 threads(num_hash_f, max(1, OPTIMAL_THREADS_PER_BLOCK / num_hash_f)); + dim3 blocks(num_query / max(1, OPTIMAL_THREADS_PER_BLOCK / num_hash_f), batch_size); + extract_query_info_cuda_kernel<<>>( + query_mask_ptr, + query_hash_code_ptr, + count_sort_table_ptr, + query_info_ptr, + batch_size, + num_hash_f, + hashtable_capacity, + num_query + ); + } + { + dim3 threads(WARP_SIZE, OPTIMAL_THREADS_PER_BLOCK / WARP_SIZE); + dim3 blocks(num_query, num_hash_f, batch_size); + int shared_mem = (weight_dim + WARP_SIZE) * sizeof(float); + lsh_weighted_cumulation_ver2_step2_cuda_kernel<<>>( + query_mask_ptr, + query_info_ptr, + key_sorted_idxes_ptr, + query_weight_ptr, + key_weight_ptr, + value_ptr, + cumulation_value_ptr, + batch_size, + num_hash_f, + num_query, + num_key, + value_dim, + weight_dim + ); + } + } + + return cumulation_value; + +} + +at::Tensor lsh_weighted_cumulation_ver3_kernel( + at::Tensor query_mask, + at::Tensor query_hash_code, + at::Tensor query_weight, + at::Tensor key_mask, + at::Tensor key_hash_code, + at::Tensor key_weight, + at::Tensor value, + int hashtable_capacity, + bool use_cuda +) { + + int batch_size = query_hash_code.size(0); + int num_hash_f = query_hash_code.size(2); + + int num_query = query_hash_code.size(1); + int num_key = key_hash_code.size(1); + int value_dim = value.size(2); + int weight_dim = query_weight.size(2); + + at::Tensor count_sort_table = at::zeros({batch_size, num_hash_f, hashtable_capacity}, query_hash_code.options()); + at::Tensor query_sorted_idxes = at::zeros({batch_size, num_hash_f, num_query}, query_hash_code.options()); + at::Tensor key_info = at::zeros({batch_size, num_key, 2, num_hash_f}, query_hash_code.options()); + at::Tensor cumulation_value = at::zeros({batch_size, num_query, value_dim}, value.options()); + + if (use_cuda) { + + int *query_mask_ptr = query_mask.data_ptr(); + int *query_hash_code_ptr = query_hash_code.data_ptr(); + float *query_weight_ptr = query_weight.data_ptr(); + int *key_mask_ptr = key_mask.data_ptr(); + int *key_hash_code_ptr = key_hash_code.data_ptr(); + float *key_weight_ptr = key_weight.data_ptr(); + float *value_ptr = value.data_ptr(); + + int *count_sort_table_ptr = count_sort_table.data_ptr(); + int *query_sorted_idxes_ptr = query_sorted_idxes.data_ptr(); + int *key_info_ptr = key_info.data_ptr(); + + float *cumulation_value_ptr = cumulation_value.data_ptr(); + + { + dim3 threads_step13(num_hash_f, max(1, OPTIMAL_THREADS_PER_BLOCK / num_hash_f)); + dim3 blocks_step13(num_query / max(1, OPTIMAL_THREADS_PER_BLOCK / num_hash_f), batch_size); + dim3 threads_step2(min(hashtable_capacity, OPTIMAL_THREADS_PER_BLOCK)); + dim3 blocks_step2(num_hash_f, batch_size); + int shared_mem = hashtable_capacity * sizeof(float); + count_sort_step1_cuda_kernel<<>>( + query_mask_ptr, + query_hash_code_ptr, + count_sort_table_ptr, + batch_size, + num_hash_f, + hashtable_capacity, + num_query + ); + count_sort_step2_cuda_kernel<<>>( + count_sort_table_ptr, + batch_size, + num_hash_f, + hashtable_capacity + ); + count_sort_step3_cuda_kernel<<>>( + query_mask_ptr, + query_hash_code_ptr, + count_sort_table_ptr, + query_sorted_idxes_ptr, + batch_size, + num_hash_f, + hashtable_capacity, + num_query + ); + } + { + dim3 threads(num_hash_f, max(1, OPTIMAL_THREADS_PER_BLOCK / num_hash_f)); + dim3 blocks(num_key / max(1, OPTIMAL_THREADS_PER_BLOCK / num_hash_f), batch_size); + extract_query_info_cuda_kernel<<>>( + key_mask_ptr, + key_hash_code_ptr, + count_sort_table_ptr, + key_info_ptr, + batch_size, + num_hash_f, + hashtable_capacity, + num_key + ); + } + { + dim3 threads(WARP_SIZE, OPTIMAL_THREADS_PER_BLOCK / WARP_SIZE); + dim3 blocks(num_key, num_hash_f, batch_size); + int shared_mem = (weight_dim + value_dim + WARP_SIZE) * sizeof(float); + lsh_weighted_cumulation_ver3_step2_cuda_kernel<<>>( + query_sorted_idxes_ptr, + key_mask_ptr, + key_info_ptr, + query_weight_ptr, + key_weight_ptr, + value_ptr, + cumulation_value_ptr, + batch_size, + num_hash_f, + num_query, + num_key, + value_dim, + weight_dim + ); + } + } + + return cumulation_value; + +} + +at::Tensor lsh_weighted_cumulation_ver4_kernel( + at::Tensor query_mask, + at::Tensor query_hash_code, + at::Tensor query_weight, + at::Tensor key_mask, + at::Tensor key_hash_code, + at::Tensor key_weight, + at::Tensor value, + int hashtable_capacity, + bool use_cuda +) { + + int batch_size = query_hash_code.size(0); + int num_hash_f = query_hash_code.size(2); + + int num_query = query_hash_code.size(1); + int num_key = key_hash_code.size(1); + int value_dim = value.size(2); + int weight_dim = query_weight.size(2); + + at::Tensor count_sort_table = at::zeros({batch_size, num_hash_f, hashtable_capacity}, query_hash_code.options()); + at::Tensor query_sorted_idxes = at::zeros({batch_size, num_hash_f, num_query}, query_hash_code.options()); + at::Tensor key_info = at::zeros({batch_size, num_key, 2, num_hash_f}, query_hash_code.options()); + at::Tensor cumulation_value = at::zeros({batch_size, num_query, value_dim}, value.options()); + + if (use_cuda) { + + int *query_mask_ptr = query_mask.data_ptr(); + int *query_hash_code_ptr = query_hash_code.data_ptr(); + float *query_weight_ptr = query_weight.data_ptr(); + int *key_mask_ptr = key_mask.data_ptr(); + int *key_hash_code_ptr = key_hash_code.data_ptr(); + float *key_weight_ptr = key_weight.data_ptr(); + float *value_ptr = value.data_ptr(); + + int *count_sort_table_ptr = count_sort_table.data_ptr(); + int *query_sorted_idxes_ptr = query_sorted_idxes.data_ptr(); + int *key_info_ptr = key_info.data_ptr(); + + float *cumulation_value_ptr = cumulation_value.data_ptr(); + + { + dim3 threads_step13(num_hash_f, max(1, OPTIMAL_THREADS_PER_BLOCK / num_hash_f)); + dim3 blocks_step13(num_query / max(1, OPTIMAL_THREADS_PER_BLOCK / num_hash_f), batch_size); + dim3 threads_step2(min(hashtable_capacity, OPTIMAL_THREADS_PER_BLOCK)); + dim3 blocks_step2(num_hash_f, batch_size); + int shared_mem = hashtable_capacity * sizeof(float); + count_sort_step1_cuda_kernel<<>>( + query_mask_ptr, + query_hash_code_ptr, + count_sort_table_ptr, + batch_size, + num_hash_f, + hashtable_capacity, + num_query + ); + count_sort_step2_cuda_kernel<<>>( + count_sort_table_ptr, + batch_size, + num_hash_f, + hashtable_capacity + ); + count_sort_step3_cuda_kernel<<>>( + query_mask_ptr, + query_hash_code_ptr, + count_sort_table_ptr, + query_sorted_idxes_ptr, + batch_size, + num_hash_f, + hashtable_capacity, + num_query + ); + } + { + dim3 threads(num_hash_f, max(1, OPTIMAL_THREADS_PER_BLOCK / num_hash_f)); + dim3 blocks(num_key / max(1, OPTIMAL_THREADS_PER_BLOCK / num_hash_f), batch_size); + extract_query_info_cuda_kernel<<>>( + key_mask_ptr, + key_hash_code_ptr, + count_sort_table_ptr, + key_info_ptr, + batch_size, + num_hash_f, + hashtable_capacity, + num_key + ); + } + { + dim3 threads(WARP_SIZE, OPTIMAL_THREADS_PER_BLOCK / WARP_SIZE); + dim3 blocks(num_key, batch_size); + int shared_mem = (weight_dim + value_dim + 2 * num_hash_f) * sizeof(float); + lsh_weighted_cumulation_ver4_step2_cuda_kernel<<>>( + query_sorted_idxes_ptr, + key_mask_ptr, + key_info_ptr, + query_weight_ptr, + key_weight_ptr, + value_ptr, + cumulation_value_ptr, + batch_size, + num_hash_f, + num_query, + num_key, + value_dim, + weight_dim + ); + } + } + + return cumulation_value; + +} diff --git a/venv/lib/python3.13/site-packages/transformers/kernels/yoso/fast_lsh_cumulation.h b/venv/lib/python3.13/site-packages/transformers/kernels/yoso/fast_lsh_cumulation.h new file mode 100644 index 0000000000000000000000000000000000000000..dd48de0ed159f49ee3afe93b12aaae719fe87688 --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/kernels/yoso/fast_lsh_cumulation.h @@ -0,0 +1,71 @@ +#include +#include +#include + +std::vector fast_hash_ver1_kernel( + at::Tensor query_mask, + at::Tensor query_vector, + at::Tensor key_mask, + at::Tensor key_vector, + int num_hash_f, + int hash_code_len, + bool use_cuda +); + +at::Tensor lsh_cumulation_ver1_kernel( + at::Tensor query_mask, + at::Tensor query_hash_code, + at::Tensor key_mask, + at::Tensor key_hash_code, + at::Tensor value, + int hashtable_capacity, + bool use_cuda +); + +at::Tensor lsh_weighted_cumulation_ver1_kernel( + at::Tensor query_mask, + at::Tensor query_hash_code, + at::Tensor query_weight, + at::Tensor key_mask, + at::Tensor key_hash_code, + at::Tensor key_weight, + at::Tensor value, + int hashtable_capacity, + bool use_cuda +); + +at::Tensor lsh_weighted_cumulation_ver2_kernel( + at::Tensor query_mask, + at::Tensor query_hash_code, + at::Tensor query_weight, + at::Tensor key_mask, + at::Tensor key_hash_code, + at::Tensor key_weight, + at::Tensor value, + int hashtable_capacity, + bool use_cuda +); + +at::Tensor lsh_weighted_cumulation_ver3_kernel( + at::Tensor query_mask, + at::Tensor query_hash_code, + at::Tensor query_weight, + at::Tensor key_mask, + at::Tensor key_hash_code, + at::Tensor key_weight, + at::Tensor value, + int hashtable_capacity, + bool use_cuda +); + +at::Tensor lsh_weighted_cumulation_ver4_kernel( + at::Tensor query_mask, + at::Tensor query_hash_code, + at::Tensor query_weight, + at::Tensor key_mask, + at::Tensor key_hash_code, + at::Tensor key_weight, + at::Tensor value, + int hashtable_capacity, + bool use_cuda +); diff --git a/venv/lib/python3.13/site-packages/transformers/kernels/yoso/fast_lsh_cumulation_cuda.cu b/venv/lib/python3.13/site-packages/transformers/kernels/yoso/fast_lsh_cumulation_cuda.cu new file mode 100644 index 0000000000000000000000000000000000000000..22944e97044659f896451936c6253d5aadd7a769 --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/kernels/yoso/fast_lsh_cumulation_cuda.cu @@ -0,0 +1,825 @@ +// File from https://github.com/mlpen/YOSO/blob/main/encoders/backbones/efficient_attentions/yoso/yoso_v1/cuda/fast_lsh_cumulation_cuda.cu + +#include "fast_lsh_cumulation_cuda.h" +#include "common_cuda_device.h" +#include "common_cuda.h" +#include "common.h" +#include +////////////////////////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////// + +inline __device__ void fast_hadamard_transform(float *vector_buffer, int vector_dim, int dim_idx) { + int stride = vector_dim / 2; + while (stride > (WARP_SIZE / 2)) { + __syncthreads(); + int sign = 1 - ((dim_idx / stride) % 2) * 2; + float val1 = vector_buffer[dim_idx]; + float val2 = vector_buffer[dim_idx + sign * stride]; + __syncthreads(); + vector_buffer[dim_idx] = float(sign) * val1 + val2; + stride = stride / 2; + } + + float val = vector_buffer[dim_idx]; + #pragma unroll + for (stride = (WARP_SIZE / 2); stride > 0; stride = stride / 2) { + int sign = 1 - ((dim_idx / stride) % 2) * 2; + val = float(sign) * val + __shfl_xor_sync(FULL_MASK, val, stride); + } + vector_buffer[dim_idx] = val; +} + +__global__ void fast_hash_ver1_cuda_kernel( + int *mask, // [batch_size, num_vector] + float *vector, // [batch_size, num_vector, vector_dim] + int *Dmat, // [batch_size, 3, num_part, vector_dim] + int *hash_code, // [batch_size, num_vector, num_hash_f] + int batch_size, + int num_vector, + int vector_dim, + int num_part, + int num_hash_f, + int hash_code_len +) { + + int batch_idx = blockIdx.z; + int vector_idx = blockIdx.y; + int part_idx = blockIdx.x; + + int dim_idx = threadIdx.x; + + int batch_idx__vector_idx = batch_idx * num_vector + vector_idx; + if (mask[batch_idx__vector_idx] == 0) { + return; + } + + extern __shared__ float buffer[]; + float *vector_buffer = buffer; + + vector_buffer[dim_idx] = vector[batch_idx__vector_idx * vector_dim + dim_idx]; + + vector_buffer[dim_idx] = vector_buffer[dim_idx] * (float)Dmat[((batch_idx * 3 + 0) * num_part + part_idx) * vector_dim + dim_idx]; + fast_hadamard_transform(vector_buffer, vector_dim, dim_idx); + vector_buffer[dim_idx] = vector_buffer[dim_idx] * (float)Dmat[((batch_idx * 3 + 1) * num_part + part_idx) * vector_dim + dim_idx]; + fast_hadamard_transform(vector_buffer, vector_dim, dim_idx); + vector_buffer[dim_idx] = vector_buffer[dim_idx] * (float)Dmat[((batch_idx * 3 + 2) * num_part + part_idx) * vector_dim + dim_idx]; + fast_hadamard_transform(vector_buffer, vector_dim, dim_idx); + + int num_hash_per_part = vector_dim / hash_code_len; + if (hash_code_len == 8 || hash_code_len == 16) { + int code = select(vector_buffer[dim_idx] > 0, 1 << (dim_idx % hash_code_len), 0); + for (int offset = 1; offset < hash_code_len; offset = offset * 2) { + code += __shfl_xor_sync(FULL_MASK, code, offset); + } + if (dim_idx % hash_code_len == 0) { + int hash_f_idx = part_idx * num_hash_per_part + dim_idx / hash_code_len; + if (hash_f_idx < num_hash_f) { + hash_code[batch_idx__vector_idx * num_hash_f + hash_f_idx] = code; + } + } + } else { + vector_buffer[dim_idx] = select(vector_buffer[dim_idx] > 0, 1 << (dim_idx % hash_code_len), 0); + __syncthreads(); + if (dim_idx < num_hash_per_part) { + int code = 0; + for (int i = 0; i < hash_code_len; i++) { + code += vector_buffer[dim_idx * hash_code_len + i]; + } + int hash_f_idx = part_idx * num_hash_per_part + dim_idx; + if (hash_f_idx < num_hash_f) { + hash_code[batch_idx__vector_idx * num_hash_f + hash_f_idx] = code; + } + } + } +} + +__global__ void lsh_cumulation_ver1_step1_cuda_kernel( + int *key_mask, // [batch_size, num_key] + int *key_hash_code, // [batch_size, num_key, num_hash_f] + float *value, // [batch_size, num_key, value_dim] + float *hashtable_value, // [batch_size, num_hash_f, hashtable_capacity, WARP_SIZE] + int batch_size, + int num_hash_f, + int hashtable_capacity, + int num_key, + int value_dim, + int offset_warp +) { + + int warp_thread_idx = threadIdx.x; + + int batch_idx = blockIdx.y; + int key_idx = blockIdx.x * blockDim.y + threadIdx.y; + + int batch_idx__key_idx = batch_idx * num_key + key_idx; + if (key_mask[batch_idx__key_idx] == 0) { + return; + } + + if (num_hash_f > WARP_SIZE) { + float warp_value = value[batch_idx__key_idx * value_dim + offset_warp + warp_thread_idx]; + for (int hash_f_start = 0; hash_f_start < num_hash_f; hash_f_start = hash_f_start + WARP_SIZE) { + int warp_hashcode = key_hash_code[batch_idx__key_idx * num_hash_f + hash_f_start + warp_thread_idx]; + #pragma unroll + for (int hash_f_offset = 0; hash_f_offset < WARP_SIZE; hash_f_offset++) { + int current_hashcode = warp_hashcode; + current_hashcode = __shfl_sync(FULL_MASK, current_hashcode, hash_f_offset); + int hashtable_idx = (batch_idx * num_hash_f + (hash_f_start + hash_f_offset)) * hashtable_capacity + current_hashcode; + atomicAdd(&hashtable_value[hashtable_idx * WARP_SIZE + warp_thread_idx], warp_value); + } + } + } else { + float warp_value = value[batch_idx__key_idx * value_dim + offset_warp + warp_thread_idx]; + int warp_hashcode = 0; + if (warp_thread_idx < num_hash_f) { + warp_hashcode = key_hash_code[batch_idx__key_idx * num_hash_f + warp_thread_idx]; + } + for (int hash_f_idx = 0; hash_f_idx < num_hash_f; hash_f_idx++) { + int current_hashcode = warp_hashcode; + current_hashcode = __shfl_sync(FULL_MASK, current_hashcode, hash_f_idx); + int hashtable_idx = (batch_idx * num_hash_f + hash_f_idx) * hashtable_capacity + current_hashcode; + atomicAdd(&hashtable_value[hashtable_idx * WARP_SIZE + warp_thread_idx], warp_value); + } + } + +} + +__global__ void lsh_cumulation_ver1_step2_cuda_kernel( + int *query_mask, // [batch_size, num_query] + int *query_hash_code, // [batch_size, num_query, num_hash_f] + float *hashtable_value, // [batch_size, num_hash_f, hashtable_capacity, WARP_SIZE] + float *cumulation_value, // [batch_size, num_query, value_dim] + int batch_size, + int num_hash_f, + int hashtable_capacity, + int num_query, + int value_dim, + int offset_warp +) { + + int warp_thread_idx = threadIdx.x; + + int batch_idx = blockIdx.y; + int query_idx = blockIdx.x * blockDim.y + threadIdx.y; + + int batch_idx__query_idx = batch_idx * num_query + query_idx; + if (query_mask[batch_idx__query_idx] == 0) { + return; + } + + if (num_hash_f > WARP_SIZE) { + float warp_value = 0; + for (int hash_f_start = 0; hash_f_start < num_hash_f; hash_f_start = hash_f_start + WARP_SIZE) { + int warp_hashcode = query_hash_code[batch_idx__query_idx * num_hash_f + hash_f_start + warp_thread_idx]; + #pragma unroll + for (int hash_f_offset = 0; hash_f_offset < WARP_SIZE; hash_f_offset++) { + int current_hashcode = warp_hashcode; + current_hashcode = __shfl_sync(FULL_MASK, current_hashcode, hash_f_offset); + int hashtable_idx = (batch_idx * num_hash_f + (hash_f_start + hash_f_offset)) * hashtable_capacity + current_hashcode; + warp_value = warp_value + hashtable_value[hashtable_idx * WARP_SIZE + warp_thread_idx]; + } + } + cumulation_value[batch_idx__query_idx * value_dim + offset_warp + warp_thread_idx] = warp_value / float(num_hash_f); + } else { + float warp_value = 0; + int warp_hashcode = 0; + if (warp_thread_idx < num_hash_f) { + warp_hashcode = query_hash_code[batch_idx__query_idx * num_hash_f + warp_thread_idx]; + } + for (int hash_f_idx = 0; hash_f_idx < num_hash_f; hash_f_idx++) { + int current_hashcode = warp_hashcode; + current_hashcode = __shfl_sync(FULL_MASK, current_hashcode, hash_f_idx); + int hashtable_idx = (batch_idx * num_hash_f + hash_f_idx) * hashtable_capacity + current_hashcode; + warp_value = warp_value + hashtable_value[hashtable_idx * WARP_SIZE + warp_thread_idx]; + } + cumulation_value[batch_idx__query_idx * value_dim + offset_warp + warp_thread_idx] = warp_value / float(num_hash_f); + } + +} + +__global__ void lsh_weighted_cumulation_ver1_step1_cuda_kernel( + int *key_mask, // [batch_size, num_key] + int *key_hash_code, // [batch_size, num_key, num_hash_f] + float *key_weight, // [batch_size, num_key, weight_dim] + float *value, // [batch_size, num_key, value_dim] + float *hashtable_value, // [batch_size, num_hash_f, hashtable_capacity, WARP_SIZE] + int batch_size, + int num_hash_f, + int hashtable_capacity, + int num_key, + int value_dim, + int weight_dim, + int offset_warp, + int weight_idx +) { + + int warp_thread_idx = threadIdx.x; + + int batch_idx = blockIdx.y; + int key_idx = blockIdx.x * blockDim.y + threadIdx.y; + + int batch_idx__key_idx = batch_idx * num_key + key_idx; + if (key_mask[batch_idx__key_idx] == 0) { + return; + } + + if (num_hash_f > WARP_SIZE) { + float warp_value = key_weight[batch_idx__key_idx * weight_dim + weight_idx] * value[batch_idx__key_idx * value_dim + offset_warp + warp_thread_idx]; + for (int hash_f_start = 0; hash_f_start < num_hash_f; hash_f_start = hash_f_start + WARP_SIZE) { + int warp_hashcode = key_hash_code[batch_idx__key_idx * num_hash_f + hash_f_start + warp_thread_idx]; + #pragma unroll + for (int hash_f_offset = 0; hash_f_offset < WARP_SIZE; hash_f_offset++) { + int current_hashcode = warp_hashcode; + current_hashcode = __shfl_sync(FULL_MASK, current_hashcode, hash_f_offset); + int hashtable_idx = (batch_idx * num_hash_f + (hash_f_start + hash_f_offset)) * hashtable_capacity + current_hashcode; + atomicAdd(&hashtable_value[hashtable_idx * WARP_SIZE + warp_thread_idx], warp_value); + } + } + } else { + float warp_value = key_weight[batch_idx__key_idx * weight_dim + weight_idx] * value[batch_idx__key_idx * value_dim + offset_warp + warp_thread_idx]; + int warp_hashcode = 0; + if (warp_thread_idx < num_hash_f) { + warp_hashcode = key_hash_code[batch_idx__key_idx * num_hash_f + warp_thread_idx]; + } + for (int hash_f_idx = 0; hash_f_idx < num_hash_f; hash_f_idx++) { + int current_hashcode = warp_hashcode; + current_hashcode = __shfl_sync(FULL_MASK, current_hashcode, hash_f_idx); + int hashtable_idx = (batch_idx * num_hash_f + hash_f_idx) * hashtable_capacity + current_hashcode; + atomicAdd(&hashtable_value[hashtable_idx * WARP_SIZE + warp_thread_idx], warp_value); + } + } + +} + +__global__ void lsh_weighted_cumulation_ver1_step2_cuda_kernel( + int *query_mask, // [batch_size, num_query] + int *query_hash_code, // [batch_size, num_query, num_hash_f] + float *query_weight, // [batch_size, num_query, weight_dim] + float *hashtable_value, // [batch_size, num_hash_f, hashtable_capacity, WARP_SIZE] + float *cumulation_value, // [batch_size, num_query, value_dim] + int batch_size, + int num_hash_f, + int hashtable_capacity, + int num_query, + int value_dim, + int weight_dim, + int offset_warp, + int weight_idx +) { + + int warp_thread_idx = threadIdx.x; + + int batch_idx = blockIdx.y; + int query_idx = blockIdx.x * blockDim.y + threadIdx.y; + + int batch_idx__query_idx = batch_idx * num_query + query_idx; + if (query_mask[batch_idx__query_idx] == 0) { + return; + } + + if (num_hash_f > WARP_SIZE) { + float warp_value = 0; + for (int hash_f_start = 0; hash_f_start < num_hash_f; hash_f_start = hash_f_start + WARP_SIZE) { + int warp_hashcode = query_hash_code[batch_idx__query_idx * num_hash_f + hash_f_start + warp_thread_idx]; + #pragma unroll + for (int hash_f_offset = 0; hash_f_offset < WARP_SIZE; hash_f_offset++) { + int current_hashcode = warp_hashcode; + current_hashcode = __shfl_sync(FULL_MASK, current_hashcode, hash_f_offset); + int hashtable_idx = (batch_idx * num_hash_f + (hash_f_start + hash_f_offset)) * hashtable_capacity + current_hashcode; + warp_value = warp_value + hashtable_value[hashtable_idx * WARP_SIZE + warp_thread_idx]; + } + } + float warp_weight = query_weight[batch_idx__query_idx * weight_dim + weight_idx]; + cumulation_value[batch_idx__query_idx * value_dim + offset_warp + warp_thread_idx] += warp_weight * warp_value / float(num_hash_f); + } else { + float warp_value = 0; + int warp_hashcode = 0; + if (warp_thread_idx < num_hash_f) { + warp_hashcode = query_hash_code[batch_idx__query_idx * num_hash_f + warp_thread_idx]; + } + for (int hash_f_idx = 0; hash_f_idx < num_hash_f; hash_f_idx++) { + int current_hashcode = warp_hashcode; + current_hashcode = __shfl_sync(FULL_MASK, current_hashcode, hash_f_idx); + int hashtable_idx = (batch_idx * num_hash_f + hash_f_idx) * hashtable_capacity + current_hashcode; + warp_value = warp_value + hashtable_value[hashtable_idx * WARP_SIZE + warp_thread_idx]; + } + float warp_weight = query_weight[batch_idx__query_idx * weight_dim + weight_idx]; + cumulation_value[batch_idx__query_idx * value_dim + offset_warp + warp_thread_idx] += warp_weight * warp_value / float(num_hash_f); + } + +} + +__global__ void count_sort_step1_cuda_kernel( + int *key_mask, // [batch_size, num_key] + int *key_hash_code, // [batch_size, num_key, num_hash_f] + int *count_sort_table, // [batch_size, num_hash_f, hashtable_capacity] + int batch_size, + int num_hash_f, + int hashtable_capacity, + int num_key +) { + + int batch_idx = blockIdx.y; + int key_idx = blockIdx.x * blockDim.y + threadIdx.y; + int hash_f_idx = threadIdx.x; + + int batch_idx__key_idx = batch_idx * num_key + key_idx; + if (key_mask[batch_idx__key_idx] == 0) { + return; + } + + int hash_code = key_hash_code[batch_idx__key_idx * num_hash_f + hash_f_idx]; + atomicAdd(&count_sort_table[(batch_idx * num_hash_f + hash_f_idx) * hashtable_capacity + hash_code], 1); + +} + +__global__ void count_sort_step2_cuda_kernel( + int *count_sort_table, // [batch_size, num_hash_f, hashtable_capacity] + int batch_size, + int num_hash_f, + int hashtable_capacity +) { + + int batch_idx = blockIdx.y; + int hash_f_idx = blockIdx.x; + + int num_threads = blockDim.x; + int thread_id = threadIdx.x; + + int batch_idx__hash_f_idx = batch_idx * num_hash_f + hash_f_idx; + + extern __shared__ float buffer[]; + int *table_buffer = (int*)buffer; + + if (thread_id == 0) { + table_buffer[0] = 0; + } + copy_data(&count_sort_table[batch_idx__hash_f_idx * hashtable_capacity], &table_buffer[1], hashtable_capacity - 1, num_threads, thread_id); + + for (int table_idx_start = 0; table_idx_start < hashtable_capacity; table_idx_start = table_idx_start + num_threads) { + int thread_value = table_buffer[table_idx_start + thread_id]; + int next_thread_value = 0; + for (int offset = 1; offset < WARP_SIZE; offset = offset << 1) { + next_thread_value = __shfl_up_sync(FULL_MASK, thread_value, offset); + if (thread_id % WARP_SIZE >= offset) { + thread_value = thread_value + next_thread_value; + } + } + table_buffer[table_idx_start + thread_id] = thread_value; + } + __syncthreads(); + + if (hashtable_capacity > WARP_SIZE) { + if (thread_id < WARP_SIZE) { + for (int table_idx_start = WARP_SIZE; table_idx_start < hashtable_capacity; table_idx_start = table_idx_start + WARP_SIZE) { + table_buffer[table_idx_start + thread_id] += table_buffer[table_idx_start - 1]; + } + } + } + + copy_data(table_buffer, &count_sort_table[batch_idx__hash_f_idx * hashtable_capacity], hashtable_capacity, num_threads, thread_id); + +} + + +__global__ void count_sort_step3_cuda_kernel( + int *key_mask, // [batch_size, num_key] + int *key_hash_code, // [batch_size, num_key, num_hash_f] + int *count_sort_table, // [batch_size, num_hash_f, hashtable_capacity] + int *key_sorted_idxes, // [batch_size, num_hash_f, num_key] + int batch_size, + int num_hash_f, + int hashtable_capacity, + int num_key +) { + + int batch_idx = blockIdx.y; + int key_idx = blockIdx.x * blockDim.y + threadIdx.y; + int hash_f_idx = threadIdx.x; + + int batch_idx__key_idx = batch_idx * num_key + key_idx; + if (key_mask[batch_idx__key_idx] == 0) { + return; + } + + int batch_idx__hash_f_idx = batch_idx * num_hash_f + hash_f_idx; + + int hash_code = key_hash_code[batch_idx__key_idx * num_hash_f + hash_f_idx]; + int sort_idx = atomicAdd(&count_sort_table[batch_idx__hash_f_idx * hashtable_capacity + hash_code], 1); + key_sorted_idxes[batch_idx__hash_f_idx * num_key + sort_idx] = key_idx; + +} + +__global__ void extract_query_info_cuda_kernel( + int *query_mask, // [batch_size, num_query] + int *query_hash_code, // [batch_size, num_query, num_hash_f] + int *count_sort_table, // [batch_size, num_hash_f, hashtable_capacity] + int *query_info, // [batch_size, num_query, 2, num_hash_f] + int batch_size, + int num_hash_f, + int hashtable_capacity, + int num_query +) { + + int batch_idx = blockIdx.y; + int query_idx = blockIdx.x * blockDim.y + threadIdx.y; + int hash_f_idx = threadIdx.x; + + int batch_idx__query_idx = batch_idx * num_query + query_idx; + if (query_mask[batch_idx__query_idx] == 0) { + return; + } + + int hash_code = query_hash_code[batch_idx__query_idx * num_hash_f + hash_f_idx]; + int batch_idx__hash_f_idx__hash_code = (batch_idx * num_hash_f + hash_f_idx) * hashtable_capacity + hash_code; + + int key_offset = select(hash_code == 0, 0, count_sort_table[batch_idx__hash_f_idx__hash_code - 1]); + int key_count = count_sort_table[batch_idx__hash_f_idx__hash_code] - key_offset; + + query_info[batch_idx__query_idx * 2 * num_hash_f + hash_f_idx] = key_offset; + query_info[(batch_idx__query_idx * 2 + 1) * num_hash_f + hash_f_idx] = key_count; + +} + +__global__ void lsh_weighted_cumulation_ver2_step2_cuda_kernel( + int *query_mask, // [batch_size, num_query] + int *query_info, // [batch_size, num_query, 2, num_hash_f] + int *key_sorted_idxes, // [batch_size, num_hash_f, num_key] + float *query_weight, // [batch_size, num_query, weight_dim] + float *key_weight, // [batch_size, num_key, weight_dim] + float *value, // [batch_size, num_key, value_dim] + float *cumulation_value, // [batch_size, num_query, value_dim] + int batch_size, + int num_hash_f, + int num_query, + int num_key, + int value_dim, + int weight_dim +) { + + int batch_idx = blockIdx.z; + int hash_f_idx = blockIdx.y; + int query_idx = blockIdx.x; + + int num_threads = blockDim.y * blockDim.x; + int thread_id = threadIdx.y * blockDim.x + threadIdx.x; + + int num_warps = blockDim.y; + int warp_idx = threadIdx.y; + int warp_thread_idx = threadIdx.x; + + int batch_idx__query_idx = batch_idx * num_query + query_idx; + if (query_mask[batch_idx__query_idx] == 0) { + return; + } + + int key_offset = query_info[batch_idx__query_idx * 2 * num_hash_f + hash_f_idx]; + int key_count = query_info[(batch_idx__query_idx * 2 + 1) * num_hash_f + hash_f_idx]; + + if (key_count == 0) { + return; + } + + extern __shared__ float buffer[]; + + if (key_count == 1) { + if (warp_idx == 0) { + int key_idx = key_sorted_idxes[(batch_idx * num_hash_f + hash_f_idx) * num_key + key_offset]; + int batch_idx__key_idx = batch_idx * num_key + key_idx; + float weight = 0; + for (int weight_offset = 0; weight_offset < weight_dim; weight_offset = weight_offset + WARP_SIZE) { + int weight_dim_idx = weight_offset + warp_thread_idx; + float val = query_weight[batch_idx__query_idx * weight_dim + weight_dim_idx] * key_weight[batch_idx__key_idx * weight_dim + weight_dim_idx]; + #pragma unroll + for (int offset = 1; offset < WARP_SIZE; offset = offset << 1) { + val += __shfl_xor_sync(FULL_MASK, val, offset); + } + weight = weight + val; + } + weight = weight / float(num_hash_f); + for (int value_offset = 0; value_offset < value_dim; value_offset = value_offset + WARP_SIZE) { + int value_dim_idx = value_offset + warp_thread_idx; + float val = value[batch_idx__key_idx * value_dim + value_dim_idx]; + atomicAdd(&cumulation_value[batch_idx__query_idx * value_dim + value_dim_idx], weight * val); + } + } + } else { + float *weight_buffer = buffer; + int *key_idxes_buffer = (int*)&buffer[weight_dim]; + + copy_data_nonblocking(&query_weight[batch_idx__query_idx * weight_dim], weight_buffer, weight_dim, num_threads, thread_id); + + while (key_count > 0) { + int work_size = min(WARP_SIZE, key_count); + copy_data_nonblocking(&key_sorted_idxes[(batch_idx * num_hash_f + hash_f_idx) * num_key + key_offset], key_idxes_buffer, work_size, num_threads, thread_id); + __syncthreads(); + for (int work_offset = 0; work_offset < WARP_SIZE; work_offset = work_offset + num_warps) { + int work_idx = work_offset + warp_idx; + if (work_idx < key_count) { + int key_idx = key_idxes_buffer[work_idx]; + int batch_idx__key_idx = batch_idx * num_key + key_idx; + float weight = 0; + for (int weight_offset = 0; weight_offset < weight_dim; weight_offset = weight_offset + WARP_SIZE) { + int weight_dim_idx = weight_offset + warp_thread_idx; + float val = weight_buffer[weight_dim_idx] * key_weight[batch_idx__key_idx * weight_dim + weight_dim_idx]; + #pragma unroll + for (int offset = 1; offset < WARP_SIZE; offset = offset << 1) { + val += __shfl_xor_sync(FULL_MASK, val, offset); + } + weight = weight + val; + } + weight = weight / float(num_hash_f); + for (int value_offset = 0; value_offset < value_dim; value_offset = value_offset + WARP_SIZE) { + int value_dim_idx = value_offset + warp_thread_idx; + float val = value[batch_idx__key_idx * value_dim + value_dim_idx]; + atomicAdd(&cumulation_value[batch_idx__query_idx * value_dim + value_dim_idx], weight * val); + } + } + } + key_count = key_count - work_size; + key_offset = key_offset + work_size; + } + } + +} + +__global__ void lsh_weighted_cumulation_ver3_step2_cuda_kernel( + int *query_sorted_idxes, // [batch_size, num_hash_f, num_query] + int *key_mask, // [batch_size, num_key] + int *key_info, // [batch_size, num_key, 2, num_hash_f] + float *query_weight, // [batch_size, num_query, weight_dim] + float *key_weight, // [batch_size, num_key, weight_dim] + float *value, // [batch_size, num_key, value_dim] + float *cumulation_value, // [batch_size, num_query, value_dim] + int batch_size, + int num_hash_f, + int num_query, + int num_key, + int value_dim, + int weight_dim +) { + + int batch_idx = blockIdx.z; + int hash_f_idx = blockIdx.y; + int key_idx = blockIdx.x; + + int num_threads = blockDim.y * blockDim.x; + int thread_id = threadIdx.y * blockDim.x + threadIdx.x; + + int num_warps = blockDim.y; + int warp_idx = threadIdx.y; + int warp_thread_idx = threadIdx.x; + + int batch_idx__key_idx = batch_idx * num_key + key_idx; + if (key_mask[batch_idx__key_idx] == 0) { + return; + } + + int query_offset = key_info[batch_idx__key_idx * 2 * num_hash_f + hash_f_idx]; + int query_count = key_info[(batch_idx__key_idx * 2 + 1) * num_hash_f + hash_f_idx]; + + if (query_count == 0) { + return; + } + + extern __shared__ float buffer[]; + + if (query_count == 1) { + if (warp_idx == 0) { + int query_idx = query_sorted_idxes[(batch_idx * num_hash_f + hash_f_idx) * num_query + query_offset]; + int batch_idx__query_idx = batch_idx * num_query + query_idx; + float weight = 0; + for (int weight_offset = 0; weight_offset < weight_dim; weight_offset = weight_offset + WARP_SIZE) { + int weight_dim_idx = weight_offset + warp_thread_idx; + float val = key_weight[batch_idx__key_idx * weight_dim + weight_dim_idx] * query_weight[batch_idx__query_idx * weight_dim + weight_dim_idx]; + #pragma unroll + for (int offset = 1; offset < WARP_SIZE; offset = offset << 1) { + val += __shfl_xor_sync(FULL_MASK, val, offset); + } + weight = weight + val; + } + weight = weight / float(num_hash_f); + for (int value_offset = 0; value_offset < value_dim; value_offset = value_offset + WARP_SIZE) { + int value_dim_idx = value_offset + warp_thread_idx; + float val = value[batch_idx__key_idx * value_dim + value_dim_idx]; + atomicAdd(&cumulation_value[batch_idx__query_idx * value_dim + value_dim_idx], weight * val); + } + } + } else { + float *weight_buffer = buffer; + float *value_buffer = &buffer[weight_dim]; + int *query_idxes_buffer = (int*)&buffer[weight_dim + value_dim]; + + copy_data_nonblocking(&key_weight[batch_idx__key_idx * weight_dim], weight_buffer, weight_dim, num_threads, thread_id); + copy_data_nonblocking(&value[batch_idx__key_idx * value_dim], value_buffer, value_dim, num_threads, thread_id); + + while (query_count > 0) { + int work_size = min(WARP_SIZE, query_count); + copy_data_nonblocking(&query_sorted_idxes[(batch_idx * num_hash_f + hash_f_idx) * num_query + query_offset], query_idxes_buffer, work_size, num_threads, thread_id); + __syncthreads(); + for (int work_offset = 0; work_offset < WARP_SIZE; work_offset = work_offset + num_warps) { + int work_idx = work_offset + warp_idx; + if (work_idx < query_count) { + int query_idx = query_idxes_buffer[work_idx]; + int batch_idx__query_idx = batch_idx * num_query + query_idx; + float weight = 0; + for (int weight_offset = 0; weight_offset < weight_dim; weight_offset = weight_offset + WARP_SIZE) { + int weight_dim_idx = weight_offset + warp_thread_idx; + float val = weight_buffer[weight_dim_idx] * query_weight[batch_idx__query_idx * weight_dim + weight_dim_idx]; + #pragma unroll + for (int offset = 1; offset < WARP_SIZE; offset = offset << 1) { + val += __shfl_xor_sync(FULL_MASK, val, offset); + } + weight = weight + val; + } + weight = weight / float(num_hash_f); + for (int value_offset = 0; value_offset < value_dim; value_offset = value_offset + WARP_SIZE) { + int value_dim_idx = value_offset + warp_thread_idx; + float val = value_buffer[value_dim_idx]; + atomicAdd(&cumulation_value[batch_idx__query_idx * value_dim + value_dim_idx], weight * val); + } + } + } + query_count = query_count - work_size; + query_offset = query_offset + work_size; + } + } + +} + +__global__ void lsh_weighted_cumulation_ver4_step2_cuda_kernel( + int *query_sorted_idxes, // [batch_size, num_hash_f, num_query] + int *key_mask, // [batch_size, num_key] + int *key_info, // [batch_size, num_key, 2, num_hash_f] + float *query_weight, // [batch_size, num_query, weight_dim] + float *key_weight, // [batch_size, num_key, weight_dim] + float *value, // [batch_size, num_key, value_dim] + float *cumulation_value, // [batch_size, num_query, value_dim] + int batch_size, + int num_hash_f, + int num_query, + int num_key, + int value_dim, + int weight_dim +) { + + int batch_idx = blockIdx.y; + int key_idx = blockIdx.x; + + int num_threads = blockDim.y * blockDim.x; + int thread_id = threadIdx.y * blockDim.x + threadIdx.x; + + int num_warps = blockDim.y; + int warp_idx = threadIdx.y; + int warp_thread_idx = threadIdx.x; + + int batch_idx__key_idx = batch_idx * num_key + key_idx; + if (key_mask[batch_idx__key_idx] == 0) { + return; + } + + extern __shared__ float buffer[]; + float *weight_buffer = buffer; + float *value_buffer = &buffer[weight_dim]; + int *key_info_buffer = (int*)&buffer[weight_dim + value_dim]; + + copy_data_nonblocking(&key_weight[batch_idx__key_idx * weight_dim], weight_buffer, weight_dim, num_threads, thread_id); + copy_data_nonblocking(&value[batch_idx__key_idx * value_dim], value_buffer, value_dim, num_threads, thread_id); + copy_data_nonblocking(&key_info[batch_idx__key_idx * 2 * num_hash_f], key_info_buffer, 2 * num_hash_f, num_threads, thread_id); + + int *query_offset_buffer = key_info_buffer; + int *query_count_buffer = &key_info_buffer[num_hash_f]; + + const int hashtable_size = 1024 + OPTIMAL_THREADS_PER_BLOCK; + __shared__ int hashtable_query[hashtable_size]; + __shared__ int hashtable_count[hashtable_size]; + __shared__ int inserted_query[hashtable_size]; + __shared__ int query_counter[1]; + + int hash_f_idx_base = 0; + + while (true) { + + init_buffer_nonblocking(EMPTY_VALUE, hashtable_query, hashtable_size, num_threads, thread_id); + init_buffer_nonblocking(0, hashtable_count, hashtable_size, num_threads, thread_id); + init_buffer_nonblocking(EMPTY_VALUE, inserted_query, hashtable_size, num_threads, thread_id); + init_buffer_nonblocking(0, query_counter, 1, num_threads, thread_id); + __syncthreads(); + + while (hash_f_idx_base < num_hash_f) { + + int hash_f_idx = hash_f_idx_base + warp_idx; + int batch_idx__hash_f_idx = batch_idx * num_hash_f + hash_f_idx; + + int stop_flag = 0; + + int query_offset = query_offset_buffer[hash_f_idx]; + int query_count = query_count_buffer[hash_f_idx]; + + while (query_count > 0) { + + int work_size = min(query_count, WARP_SIZE); + + // try inserting query to set and check whether the query is new + int found_new_query = 0; + int query_idx = -1; + if (warp_thread_idx < work_size) { + query_idx = query_sorted_idxes[batch_idx__hash_f_idx * num_query + query_offset + warp_thread_idx]; + int slot = set_insert(hashtable_query, hashtable_size, query_idx); + if (slot >= 0) { + found_new_query = atomicAdd(&hashtable_count[slot], 1) == 0; + } + } + + // compute cumulative offset + int position_offset = found_new_query; + int next_position_offset = 0; + #pragma unroll + for (int offset = 1; offset < WARP_SIZE; offset = offset << 1) { + next_position_offset = __shfl_up_sync(FULL_MASK, position_offset, offset); + if (thread_id % WARP_SIZE >= offset) { + position_offset = position_offset + next_position_offset; + } + } + + // get the inserted query list end index + int inserted_query_base = 0; + if (thread_id % WARP_SIZE == WARP_SIZE - 1) { + inserted_query_base = atomicAdd(query_counter, position_offset); + } + inserted_query_base = __shfl_sync(FULL_MASK, inserted_query_base, WARP_SIZE - 1); + + // insert new queries to list + int insert_idx = inserted_query_base + position_offset - 1; + if (found_new_query) { + inserted_query[insert_idx] = query_idx; + } + + // remove inserted queries from list + query_offset_buffer[hash_f_idx] += work_size; + query_count_buffer[hash_f_idx] -= work_size; + query_offset += work_size; + query_count -= work_size; + + // if list is almost full, stop inserting + if (inserted_query_base + OPTIMAL_THREADS_PER_BLOCK > hashtable_size) { + stop_flag = 1; + break; + } + + } + + if (stop_flag) { + break; + } + + hash_f_idx_base = hash_f_idx_base + num_warps; + + } + + __syncthreads(); + + int num_distinct_query = query_counter[0]; + + if (num_distinct_query > 0) { + for (int idx_base = 0; idx_base < num_distinct_query; idx_base = idx_base + num_warps) { + int idx = idx_base + warp_idx; + if (idx < num_distinct_query) { + int query_idx = inserted_query[idx]; + int batch_idx__query_idx = batch_idx * num_query + query_idx; + + int slot = set_lookup(hashtable_query, hashtable_size, query_idx); + int duplicate_count = hashtable_count[slot]; + + float weight = 0; + for (int weight_idx_base = 0; weight_idx_base < weight_dim; weight_idx_base = weight_idx_base + WARP_SIZE) { + int weight_dim_idx = weight_idx_base + warp_thread_idx; + float val = weight_buffer[weight_dim_idx] * query_weight[batch_idx__query_idx * weight_dim + weight_dim_idx]; + #pragma unroll + for (int offset = 1; offset < WARP_SIZE; offset = offset << 1) { + val += __shfl_xor_sync(FULL_MASK, val, offset); + } + weight = weight + val; + } + + weight = (float)duplicate_count * weight / float(num_hash_f); + + for (int value_idx_base = 0; value_idx_base < value_dim; value_idx_base = value_idx_base + WARP_SIZE) { + int value_dim_idx = value_idx_base + warp_thread_idx; + float val = value_buffer[value_dim_idx]; + atomicAdd(&cumulation_value[batch_idx__query_idx * value_dim + value_dim_idx], weight * val); + } + } + } + } else { + + // all computation is completed if num_distinct_query == 0 + break; + + } + + __syncthreads(); + + } + +} diff --git a/venv/lib/python3.13/site-packages/transformers/kernels/yoso/fast_lsh_cumulation_cuda.h b/venv/lib/python3.13/site-packages/transformers/kernels/yoso/fast_lsh_cumulation_cuda.h new file mode 100644 index 0000000000000000000000000000000000000000..b2adc0f735358d0fcb6a056e7d19ba745977e129 --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/kernels/yoso/fast_lsh_cumulation_cuda.h @@ -0,0 +1,157 @@ +__global__ void fast_hash_ver1_cuda_kernel( + int *mask, // [batch_size, num_vector] + float *vector, // [batch_size, num_vector, vector_dim] + int *Dmat, // [3, num_part, vector_dim] + int *hash_code, // [batch_size, num_vector, num_hash_f] + int batch_size, + int num_vector, + int vector_dim, + int num_part, + int num_hash_f, + int hash_code_len +); + +__global__ void lsh_cumulation_ver1_step1_cuda_kernel( + int *key_mask, // [batch_size, num_key] + int *key_hash_code, // [batch_size, num_key, num_hash_f] + float *value, // [batch_size, num_key, value_dim] + float *hashtable_value, // [batch_size, num_hash_f, hashtable_capacity, value_dim] + int batch_size, + int num_hash_f, + int hashtable_capacity, + int num_key, + int value_dim, + int offset_warp +); + +__global__ void lsh_cumulation_ver1_step2_cuda_kernel( + int *query_mask, // [batch_size, num_query] + int *query_hash_code, // [batch_size, num_query, num_hash_f] + float *hashtable_value, // [batch_size, num_hash_f, hashtable_capacity, value_dim] + float *cumulation_value, // [batch_size, num_query, value_dim] + int batch_size, + int num_hash_f, + int hashtable_capacity, + int num_query, + int value_dim, + int offset_warp +); + +__global__ void lsh_weighted_cumulation_ver1_step1_cuda_kernel( + int *key_mask, // [batch_size, num_key] + int *key_hash_code, // [batch_size, num_key, num_hash_f] + float *key_weight, // [batch_size, num_key, weight_dim] + float *value, // [batch_size, num_key, value_dim] + float *hashtable_value, // [batch_size, num_hash_f, hashtable_capacity, WARP_SIZE] + int batch_size, + int num_hash_f, + int hashtable_capacity, + int num_key, + int value_dim, + int weight_dim, + int offset_warp, + int weight_idx +); + +__global__ void lsh_weighted_cumulation_ver1_step2_cuda_kernel( + int *query_mask, // [batch_size, num_query] + int *query_hash_code, // [batch_size, num_query, num_hash_f] + float *query_weight, // [batch_size, num_query, weight_dim] + float *hashtable_value, // [batch_size, num_hash_f, hashtable_capacity, WARP_SIZE] + float *cumulation_value, // [batch_size, num_query, value_dim] + int batch_size, + int num_hash_f, + int hashtable_capacity, + int num_query, + int value_dim, + int weight_dim, + int offset_warp, + int weight_idx +); + +__global__ void count_sort_step1_cuda_kernel( + int *key_mask, // [batch_size, num_key] + int *key_hash_code, // [batch_size, num_key, num_hash_f] + int *count_sort_table, // [batch_size, num_hash_f, hashtable_capacity] + int batch_size, + int num_hash_f, + int hashtable_capacity, + int num_key +); + +__global__ void count_sort_step2_cuda_kernel( + int *count_sort_table, // [batch_size, num_hash_f, hashtable_capacity] + int batch_size, + int num_hash_f, + int hashtable_capacity +); + +__global__ void count_sort_step3_cuda_kernel( + int *key_mask, // [batch_size, num_key] + int *key_hash_code, // [batch_size, num_key, num_hash_f] + int *count_sort_table, // [batch_size, num_hash_f, hashtable_capacity] + int *key_sorted_idxes, // [batch_size, num_hash_f, num_key] + int batch_size, + int num_hash_f, + int hashtable_capacity, + int num_key +); + +__global__ void extract_query_info_cuda_kernel( + int *query_mask, // [batch_size, num_query] + int *query_hash_code, // [batch_size, num_query, num_hash_f] + int *count_sort_table, // [batch_size, num_hash_f, hashtable_capacity] + int *query_info, // [batch_size, num_query, 2, num_hash_f] + int batch_size, + int num_hash_f, + int hashtable_capacity, + int num_query +); + +__global__ void lsh_weighted_cumulation_ver2_step2_cuda_kernel( + int *query_mask, // [batch_size, num_query] + int *query_info, // [batch_size, num_query, 2, num_hash_f] + int *key_sorted_idxes, // [batch_size, num_hash_f, num_key] + float *query_weight, // [batch_size, num_query, weight_dim] + float *key_weight, // [batch_size, num_key, weight_dim] + float *value, // [batch_size, num_key, value_dim] + float *cumulation_value, // [batch_size, num_query, value_dim] + int batch_size, + int num_hash_f, + int num_query, + int num_key, + int value_dim, + int weight_dim +); + +__global__ void lsh_weighted_cumulation_ver3_step2_cuda_kernel( + int *query_sorted_idxes, // [batch_size, num_hash_f, num_query] + int *key_mask, // [batch_size, num_key] + int *key_info, // [batch_size, num_key, 2, num_hash_f] + float *query_weight, // [batch_size, num_query, weight_dim] + float *key_weight, // [batch_size, num_key, weight_dim] + float *value, // [batch_size, num_key, value_dim] + float *cumulation_value, // [batch_size, num_query, value_dim] + int batch_size, + int num_hash_f, + int num_query, + int num_key, + int value_dim, + int weight_dim +); + +__global__ void lsh_weighted_cumulation_ver4_step2_cuda_kernel( + int *query_sorted_idxes, // [batch_size, num_hash_f, num_query] + int *key_mask, // [batch_size, num_key] + int *key_info, // [batch_size, num_key, 2, num_hash_f] + float *query_weight, // [batch_size, num_query, weight_dim] + float *key_weight, // [batch_size, num_key, weight_dim] + float *value, // [batch_size, num_key, value_dim] + float *cumulation_value, // [batch_size, num_query, value_dim] + int batch_size, + int num_hash_f, + int num_query, + int num_key, + int value_dim, + int weight_dim +); diff --git a/venv/lib/python3.13/site-packages/transformers/kernels/yoso/fast_lsh_cumulation_torch.cpp b/venv/lib/python3.13/site-packages/transformers/kernels/yoso/fast_lsh_cumulation_torch.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e150a2be604b28f600ab345a8cc9e97819cca416 --- /dev/null +++ b/venv/lib/python3.13/site-packages/transformers/kernels/yoso/fast_lsh_cumulation_torch.cpp @@ -0,0 +1,128 @@ +#include +#include +#include "fast_lsh_cumulation.h" +#include "common_cuda.h" +#include + +std::vector fast_hash( + at::Tensor query_mask, + at::Tensor query_vector, + at::Tensor key_mask, + at::Tensor key_vector, + int num_hash_f, + int hash_code_len, + bool use_cuda, + int version +) { + return fast_hash_ver1_kernel( + query_mask, + query_vector, + key_mask, + key_vector, + num_hash_f, + hash_code_len, + use_cuda + ); +} + +at::Tensor lsh_cumulation( + at::Tensor query_mask, // [batch_size, num_query] + at::Tensor query_hash_code, // [batch_size, num_query, num_hash_f] + at::Tensor key_mask, // [batch_size, num_key] + at::Tensor key_hash_code, // [batch_size, num_key, num_hash_f] + at::Tensor value, // [batch_size, num_key, value_dim] + int hashtable_capacity, + bool use_cuda, + int version +) { + return lsh_cumulation_ver1_kernel( + query_mask, + query_hash_code, + key_mask, + key_hash_code, + value, + hashtable_capacity, + use_cuda + ); +} + +at::Tensor lsh_weighted_cumulation( + at::Tensor query_mask, // [batch_size, num_query] + at::Tensor query_hash_code, // [batch_size, num_query, num_hash_f] + at::Tensor query_weight, // [batch_size, num_query, weight_dim] + at::Tensor key_mask, // [batch_size, num_key] + at::Tensor key_hash_code, // [batch_size, num_key, num_hash_f] + at::Tensor key_weight, // [batch_size, num_key, weight_dim] + at::Tensor value, // [batch_size, num_key, value_dim] + int hashtable_capacity, + bool use_cuda, + int version +) { + if (version == 1) { + return lsh_weighted_cumulation_ver1_kernel( + query_mask, + query_hash_code, + query_weight, + key_mask, + key_hash_code, + key_weight, + value, + hashtable_capacity, + use_cuda + ); + } else if (version == 2) { + return lsh_weighted_cumulation_ver2_kernel( + query_mask, + query_hash_code, + query_weight, + key_mask, + key_hash_code, + key_weight, + value, + hashtable_capacity, + use_cuda + ); + } else if (version == 3) { + return lsh_weighted_cumulation_ver3_kernel( + query_mask, + query_hash_code, + query_weight, + key_mask, + key_hash_code, + key_weight, + value, + hashtable_capacity, + use_cuda + ); + } else if (version == 4) { + return lsh_weighted_cumulation_ver4_kernel( + query_mask, + query_hash_code, + query_weight, + key_mask, + key_hash_code, + key_weight, + value, + hashtable_capacity, + use_cuda + ); + } else { + return lsh_weighted_cumulation_ver3_kernel( + query_mask, + query_hash_code, + query_weight, + key_mask, + key_hash_code, + key_weight, + value, + hashtable_capacity, + use_cuda + ); + } +} + +PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { + m.def("fast_hash", &fast_hash, "Fast Hash (CUDA)"); + m.def("lsh_cumulation", &lsh_cumulation, "LSH Cumulation (CUDA)"); + m.def("lsh_weighted_cumulation", &lsh_weighted_cumulation, "LSH Weighted Cumulation (CUDA)"); +} diff --git a/venv/lib/python3.13/site-packages/transformers/loss/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/loss/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..af3c5802971afe8f9ee0d2d790158ac68b3d55b1 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/loss/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/loss/__pycache__/loss_d_fine.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/loss/__pycache__/loss_d_fine.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4835edd1ed92fc53948d83a90b247ba5586625e0 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/loss/__pycache__/loss_d_fine.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/loss/__pycache__/loss_deformable_detr.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/loss/__pycache__/loss_deformable_detr.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..860963614800b8cbf57156705245752ea979666a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/loss/__pycache__/loss_deformable_detr.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/loss/__pycache__/loss_for_object_detection.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/loss/__pycache__/loss_for_object_detection.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..07611cc32fa8703343649882595cae1b9b86ef33 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/loss/__pycache__/loss_for_object_detection.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/loss/__pycache__/loss_grounding_dino.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/loss/__pycache__/loss_grounding_dino.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5819371c843896669efd7f3325ba8a19176c252e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/loss/__pycache__/loss_grounding_dino.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/loss/__pycache__/loss_rt_detr.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/loss/__pycache__/loss_rt_detr.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6f1178459f2816b128eb161ca4448b26d0c1ea7a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/loss/__pycache__/loss_rt_detr.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/loss/__pycache__/loss_utils.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/loss/__pycache__/loss_utils.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..475fb8d912324c7d63f7db0232cc4b1f107b3824 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/loss/__pycache__/loss_utils.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/autoformer/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/autoformer/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c61026eae1e2c34bb1df46ded4488a6d9f1a0019 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/autoformer/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/autoformer/__pycache__/configuration_autoformer.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/autoformer/__pycache__/configuration_autoformer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1f6c6a5e9dbbfb720d24815d8d2f5595ebd31637 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/autoformer/__pycache__/configuration_autoformer.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/bamba/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/bamba/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..011a168bb5afd38a384254a3c99771a0cf3722d6 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/bamba/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/bamba/__pycache__/configuration_bamba.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/bamba/__pycache__/configuration_bamba.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c07de7dbe450af70a08ea4daaaa9e6a78e5af25e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/bamba/__pycache__/configuration_bamba.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/bamba/__pycache__/modeling_bamba.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/bamba/__pycache__/modeling_bamba.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ddf10d3b78433847834a9288a26dd1cf18aeb915 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/bamba/__pycache__/modeling_bamba.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/bamba/__pycache__/modular_bamba.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/bamba/__pycache__/modular_bamba.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f3f8c8fce2b585061ba3fb7bc67df184ddbc4f05 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/bamba/__pycache__/modular_bamba.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/bark/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/bark/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6e75098f3bef2f8045edacd098b7e183147e7823 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/bark/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/bark/__pycache__/configuration_bark.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/bark/__pycache__/configuration_bark.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2d52bc7c08c9edfd1f21abca4bae0464debfc05b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/bark/__pycache__/configuration_bark.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/bark/__pycache__/generation_configuration_bark.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/bark/__pycache__/generation_configuration_bark.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c973119e069c587c477cd183d889f2e7b1627467 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/bark/__pycache__/generation_configuration_bark.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/bark/__pycache__/modeling_bark.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/bark/__pycache__/modeling_bark.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4ef907dde5e6fbc5e71901796ab07522c6160e3a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/bark/__pycache__/modeling_bark.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/bark/__pycache__/processing_bark.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/bark/__pycache__/processing_bark.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2d381a2c384815caeb1a7098dee3562e6cdd7ccc Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/bark/__pycache__/processing_bark.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/barthez/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/barthez/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d31662ea95b7b700ebf8b4379661a4fc5f0c5606 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/barthez/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/barthez/__pycache__/tokenization_barthez.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/barthez/__pycache__/tokenization_barthez.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..83cc5a7714b4f124405b7b533075e4b1d90e59c8 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/barthez/__pycache__/tokenization_barthez.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/barthez/__pycache__/tokenization_barthez_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/barthez/__pycache__/tokenization_barthez_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..79a4df60983b8c332b0c06eb28ae2362afc4b723 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/barthez/__pycache__/tokenization_barthez_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/bartpho/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/bartpho/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..002386a9489e043ea6882bd9ce2f255c6750c6d5 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/bartpho/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/bartpho/__pycache__/tokenization_bartpho.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/bartpho/__pycache__/tokenization_bartpho.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..06b554472468105a664504c018980a48dddb088a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/bartpho/__pycache__/tokenization_bartpho.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/bert/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/bert/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f80d9da30412e9d0f1fb3eca43bf41b38485e16e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/bert/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/bert/__pycache__/configuration_bert.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/bert/__pycache__/configuration_bert.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2342de5391ea86524f07a84f14d1327cca0c1048 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/bert/__pycache__/configuration_bert.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/bert/__pycache__/modeling_bert.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/bert/__pycache__/modeling_bert.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..929aa7786560476128e6b1f8b14a21d4e867b881 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/bert/__pycache__/modeling_bert.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/bert/__pycache__/modeling_flax_bert.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/bert/__pycache__/modeling_flax_bert.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e7b6761e5d75e7b7321febdb0333bafcc6c8303c Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/bert/__pycache__/modeling_flax_bert.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/bert/__pycache__/modeling_tf_bert.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/bert/__pycache__/modeling_tf_bert.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a8f0209cce26f20ef1b842cbea6cb8ad19c45ecd Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/bert/__pycache__/modeling_tf_bert.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/bert/__pycache__/tokenization_bert.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/bert/__pycache__/tokenization_bert.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6fd2b89edbb1ff18c3a6450782d145c4e0e5e4d1 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/bert/__pycache__/tokenization_bert.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/bert/__pycache__/tokenization_bert_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/bert/__pycache__/tokenization_bert_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..79b702fd793f1d75681f0ef69b5a0d8bfbd0d1d9 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/bert/__pycache__/tokenization_bert_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/bert/__pycache__/tokenization_bert_tf.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/bert/__pycache__/tokenization_bert_tf.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8785e534a8de1db89594accf8bcdfc5bccc7c356 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/bert/__pycache__/tokenization_bert_tf.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/bitnet/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/bitnet/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4d85770396480cf22bfbabbe10261f28597727ed Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/bitnet/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/bitnet/__pycache__/configuration_bitnet.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/bitnet/__pycache__/configuration_bitnet.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1c1fb76013f703021a9950914b4b074bbd419f12 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/bitnet/__pycache__/configuration_bitnet.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/bitnet/__pycache__/modeling_bitnet.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/bitnet/__pycache__/modeling_bitnet.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7ab6b8227e08a03e7e476c50f0387c751de06875 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/bitnet/__pycache__/modeling_bitnet.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/bitnet/__pycache__/modular_bitnet.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/bitnet/__pycache__/modular_bitnet.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..988265677a425db49d230da9f65efbb986cc82e9 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/bitnet/__pycache__/modular_bitnet.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/blenderbot/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/blenderbot/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a7edd4b63498c5e92c5929a99dc4327c1bb7602d Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/blenderbot/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/blenderbot/__pycache__/configuration_blenderbot.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/blenderbot/__pycache__/configuration_blenderbot.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..05c4266c68a323223288ff0b43d1ce1f930832d9 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/blenderbot/__pycache__/configuration_blenderbot.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/blenderbot/__pycache__/modeling_blenderbot.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/blenderbot/__pycache__/modeling_blenderbot.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9b872568f0377e88a606104ebeaa5fbf07050413 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/blenderbot/__pycache__/modeling_blenderbot.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/blenderbot/__pycache__/modeling_flax_blenderbot.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/blenderbot/__pycache__/modeling_flax_blenderbot.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b3879bbd8131b087b3309fc75ee6cb1b2fee82b2 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/blenderbot/__pycache__/modeling_flax_blenderbot.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/blenderbot/__pycache__/modeling_tf_blenderbot.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/blenderbot/__pycache__/modeling_tf_blenderbot.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dcb86554c16315fe6dee173c83a2c2848c6e9652 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/blenderbot/__pycache__/modeling_tf_blenderbot.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/blenderbot/__pycache__/tokenization_blenderbot.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/blenderbot/__pycache__/tokenization_blenderbot.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b433a067ac7aa85fc06f0db9a83b364ba8e3809c Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/blenderbot/__pycache__/tokenization_blenderbot.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/blenderbot/__pycache__/tokenization_blenderbot_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/blenderbot/__pycache__/tokenization_blenderbot_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..219c43ace10fd5d988e83ba11a2c247f5033484d Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/blenderbot/__pycache__/tokenization_blenderbot_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/blenderbot_small/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/blenderbot_small/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3482ff3f118c41ccc61b7ee07978eeaa2e675486 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/blenderbot_small/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/blenderbot_small/__pycache__/configuration_blenderbot_small.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/blenderbot_small/__pycache__/configuration_blenderbot_small.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9bbadf4ebf1bac2dd8fc4d745e797387e1328008 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/blenderbot_small/__pycache__/configuration_blenderbot_small.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/blenderbot_small/__pycache__/modeling_blenderbot_small.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/blenderbot_small/__pycache__/modeling_blenderbot_small.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0dd8abac6ad7149e0f3f8ddf0a394bdf0c06e8af Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/blenderbot_small/__pycache__/modeling_blenderbot_small.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/blenderbot_small/__pycache__/modeling_flax_blenderbot_small.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/blenderbot_small/__pycache__/modeling_flax_blenderbot_small.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a6977dcac213f8340667b48ca98d85baa7ca460f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/blenderbot_small/__pycache__/modeling_flax_blenderbot_small.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/blenderbot_small/__pycache__/modeling_tf_blenderbot_small.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/blenderbot_small/__pycache__/modeling_tf_blenderbot_small.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..015f01eefadf32d76c241e30870777c9c412bcfd Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/blenderbot_small/__pycache__/modeling_tf_blenderbot_small.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/blenderbot_small/__pycache__/tokenization_blenderbot_small.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/blenderbot_small/__pycache__/tokenization_blenderbot_small.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3e30dfb43066a0e64bc62ff530111872ba6a7993 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/blenderbot_small/__pycache__/tokenization_blenderbot_small.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/blenderbot_small/__pycache__/tokenization_blenderbot_small_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/blenderbot_small/__pycache__/tokenization_blenderbot_small_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e4432a0d6c21d8aca2fbe3fa30e540ad1a6fd0b8 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/blenderbot_small/__pycache__/tokenization_blenderbot_small_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/byt5/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/byt5/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7c8b261031f3526fd1817555f0249900262fc1a0 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/byt5/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/byt5/__pycache__/tokenization_byt5.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/byt5/__pycache__/tokenization_byt5.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..68df7d904f44a7bbe0d5928b34ea092ede84be87 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/byt5/__pycache__/tokenization_byt5.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/clap/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/clap/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4c62a108c8035da32edb30c673d81bea7c167a00 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/clap/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/clap/__pycache__/configuration_clap.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/clap/__pycache__/configuration_clap.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c2aed824ecd856a43b8646032cf1f9e2715d673d Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/clap/__pycache__/configuration_clap.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/clap/__pycache__/feature_extraction_clap.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/clap/__pycache__/feature_extraction_clap.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e16671003ad8b6e1ca9c37c0e9a38215c43f0e18 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/clap/__pycache__/feature_extraction_clap.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/clap/__pycache__/modeling_clap.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/clap/__pycache__/modeling_clap.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6a3215390068d4120f2294b3ee3fe2d75727b19d Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/clap/__pycache__/modeling_clap.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/clap/__pycache__/processing_clap.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/clap/__pycache__/processing_clap.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c72d18f112072a4ad4fb9f9e656a30db2f92252e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/clap/__pycache__/processing_clap.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..51e613c84f3c343222eefaf3d93b8ab60853efd9 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/configuration_clip.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/configuration_clip.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..91fe097d950344444825ace1c9643d4bd176743a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/configuration_clip.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/feature_extraction_clip.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/feature_extraction_clip.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4ce95b86ff1b446d5090a1eae8c8a136a8de6adb Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/feature_extraction_clip.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/image_processing_clip.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/image_processing_clip.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3da90c9d49324cc4d91cf17ce5719466af5d91cb Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/image_processing_clip.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/image_processing_clip_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/image_processing_clip_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9a6909e00b64167c6c201eb36d16bf07f3f1cb11 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/image_processing_clip_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/modeling_clip.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/modeling_clip.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b11334b20638d9bfad88e47f6c16be5ad553041a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/modeling_clip.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/modeling_flax_clip.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/modeling_flax_clip.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2e10cb5a08594a29a1119a0d8a3e741fb3c52862 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/modeling_flax_clip.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/modeling_tf_clip.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/modeling_tf_clip.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8ffe2735be192fbfb21710b05eebe92c4b3ceee4 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/modeling_tf_clip.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/processing_clip.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/processing_clip.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9b2120035a5b23cea71899a50cc3d3bcad1a09c0 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/processing_clip.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/tokenization_clip.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/tokenization_clip.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7b831b2772fc6c8bc73ebb556aa244208e83d5b6 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/tokenization_clip.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/tokenization_clip_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/tokenization_clip_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8910de08567a257e5ae7bf123edcf9436df674df Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/clip/__pycache__/tokenization_clip_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/clipseg/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/clipseg/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3691e0375e9d9c7e8fc6e8205735653f39fc847a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/clipseg/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/clipseg/__pycache__/configuration_clipseg.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/clipseg/__pycache__/configuration_clipseg.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f34d1a47e442f74deec3b109cd4961a2ddc21a3e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/clipseg/__pycache__/configuration_clipseg.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/clipseg/__pycache__/modeling_clipseg.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/clipseg/__pycache__/modeling_clipseg.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f61324781146c1ccf8199a88d53c5a2c96445d0e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/clipseg/__pycache__/modeling_clipseg.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/clipseg/__pycache__/processing_clipseg.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/clipseg/__pycache__/processing_clipseg.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f3efd9ff2be0592bd6835bef20eb4c52255208d2 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/clipseg/__pycache__/processing_clipseg.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/codegen/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/codegen/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a0362dbe244b7e1a5969c12ed2f0c55d8c662615 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/codegen/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/codegen/__pycache__/configuration_codegen.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/codegen/__pycache__/configuration_codegen.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8ea3fd5cd92d0cead39f1e3149aef0814db86f0b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/codegen/__pycache__/configuration_codegen.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/codegen/__pycache__/modeling_codegen.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/codegen/__pycache__/modeling_codegen.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9f11ba7223db3085e6ca50b9f435fead6686f860 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/codegen/__pycache__/modeling_codegen.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/codegen/__pycache__/tokenization_codegen.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/codegen/__pycache__/tokenization_codegen.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6f2a503e438081476fa4f415ebe03209a5d87f5f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/codegen/__pycache__/tokenization_codegen.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/codegen/__pycache__/tokenization_codegen_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/codegen/__pycache__/tokenization_codegen_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1774ae7484f1513938c9bb92acf753393b250c47 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/codegen/__pycache__/tokenization_codegen_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/cohere/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/cohere/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..34ecb0d2eb96369a70eb2ee89557c1a048196886 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/cohere/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/cohere/__pycache__/configuration_cohere.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/cohere/__pycache__/configuration_cohere.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7260d0f5a9b4e819fff52dce7bca8443660f2f1b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/cohere/__pycache__/configuration_cohere.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/cohere/__pycache__/modeling_cohere.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/cohere/__pycache__/modeling_cohere.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..64039442b7776ef533ffae846a52c2fae07bb1e6 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/cohere/__pycache__/modeling_cohere.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/cohere/__pycache__/modular_cohere.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/cohere/__pycache__/modular_cohere.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fd8d5d1bbe57518385f1191ee6dea1659c38eb53 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/cohere/__pycache__/modular_cohere.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/cohere/__pycache__/tokenization_cohere_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/cohere/__pycache__/tokenization_cohere_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a99cb4e18a26137872586eb86ffe5891dbe11723 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/cohere/__pycache__/tokenization_cohere_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/cohere2/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/cohere2/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..403b3cc7781538febee8fb4fd0771f2f61bb4abc Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/cohere2/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/cohere2/__pycache__/configuration_cohere2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/cohere2/__pycache__/configuration_cohere2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..79b0c4e137f3636e8a3593d9ae148ed915106fd1 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/cohere2/__pycache__/configuration_cohere2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/cohere2/__pycache__/modeling_cohere2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/cohere2/__pycache__/modeling_cohere2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1233d7eed6b6d1147118f9ad8a80ca21a55ad6c4 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/cohere2/__pycache__/modeling_cohere2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/cohere2/__pycache__/modular_cohere2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/cohere2/__pycache__/modular_cohere2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3d84fe171bb6d477cffd93d144368533f2ad42db Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/cohere2/__pycache__/modular_cohere2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/cohere2_vision/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/cohere2_vision/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8b7dce94f05f9e204cbccbe0709a44388f8eaaa1 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/cohere2_vision/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/cohere2_vision/__pycache__/configuration_cohere2_vision.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/cohere2_vision/__pycache__/configuration_cohere2_vision.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eee17e69b71092a409ab401c8304830db63c8ee1 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/cohere2_vision/__pycache__/configuration_cohere2_vision.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/cohere2_vision/__pycache__/image_processing_cohere2_vision_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/cohere2_vision/__pycache__/image_processing_cohere2_vision_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ac5ba11fb8899f95719125f86d09d8b4d1652ffa Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/cohere2_vision/__pycache__/image_processing_cohere2_vision_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/cohere2_vision/__pycache__/modeling_cohere2_vision.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/cohere2_vision/__pycache__/modeling_cohere2_vision.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a49c98728637986ae94065c4e49da2b80c8d2ab7 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/cohere2_vision/__pycache__/modeling_cohere2_vision.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/cohere2_vision/__pycache__/modular_cohere2_vision.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/cohere2_vision/__pycache__/modular_cohere2_vision.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4745bbc3af9fea743c0675daf076a392180e29b7 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/cohere2_vision/__pycache__/modular_cohere2_vision.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/cohere2_vision/__pycache__/processing_cohere2_vision.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/cohere2_vision/__pycache__/processing_cohere2_vision.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3ea0d244c3915893233d5f64ada85f3ef62b0f91 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/cohere2_vision/__pycache__/processing_cohere2_vision.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/colpali/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/colpali/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f5a644cc438cbcc92bd263da74e1c6947549f07d Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/colpali/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/colpali/__pycache__/configuration_colpali.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/colpali/__pycache__/configuration_colpali.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..715e50e61f9619909df9d1509be51c0359c53345 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/colpali/__pycache__/configuration_colpali.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/colpali/__pycache__/modeling_colpali.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/colpali/__pycache__/modeling_colpali.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..567ea856dd6be36bb1d9ba5b26a467d5c0256fab Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/colpali/__pycache__/modeling_colpali.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/colpali/__pycache__/modular_colpali.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/colpali/__pycache__/modular_colpali.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8574239e1b8dfb5ffa6eaea0791eed10c262cbc8 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/colpali/__pycache__/modular_colpali.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/colpali/__pycache__/processing_colpali.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/colpali/__pycache__/processing_colpali.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c07db2e45c762e7c5e6576fd95dcdc746226ac51 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/colpali/__pycache__/processing_colpali.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/convbert/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/convbert/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b8cb0e62bdb78dd7498cf3a3faddea472d9018fa Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/convbert/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/convbert/__pycache__/configuration_convbert.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/convbert/__pycache__/configuration_convbert.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e508d9815aac3535bc386f3cf68e4d24ee7909e7 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/convbert/__pycache__/configuration_convbert.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/convbert/__pycache__/modeling_convbert.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/convbert/__pycache__/modeling_convbert.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a8f4bac16902c71ea7ea7b183f6d2863883de9eb Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/convbert/__pycache__/modeling_convbert.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/convbert/__pycache__/modeling_tf_convbert.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/convbert/__pycache__/modeling_tf_convbert.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..65b677480df5229781bbe2b67b094d8d868bf831 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/convbert/__pycache__/modeling_tf_convbert.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/convbert/__pycache__/tokenization_convbert.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/convbert/__pycache__/tokenization_convbert.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b876cc125d1bbea28612ceeb23bdab5f2ee6351e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/convbert/__pycache__/tokenization_convbert.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/convbert/__pycache__/tokenization_convbert_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/convbert/__pycache__/tokenization_convbert_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1e080880234b2391a742db754049b55378f625e8 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/convbert/__pycache__/tokenization_convbert_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/ctrl/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/ctrl/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4c410c84acdf643907a92accd44ab296e4805e1b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/ctrl/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/ctrl/__pycache__/configuration_ctrl.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/ctrl/__pycache__/configuration_ctrl.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..74e2f31bdb8620b930cd5003ac30f045fd5c38ef Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/ctrl/__pycache__/configuration_ctrl.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/ctrl/__pycache__/modeling_ctrl.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/ctrl/__pycache__/modeling_ctrl.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a922d9ffa0236a11c1f0d0e778504016b808d09d Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/ctrl/__pycache__/modeling_ctrl.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/ctrl/__pycache__/modeling_tf_ctrl.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/ctrl/__pycache__/modeling_tf_ctrl.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4ced9f00f4b808085e3c1ec9fbe41e1c63390b43 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/ctrl/__pycache__/modeling_tf_ctrl.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/ctrl/__pycache__/tokenization_ctrl.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/ctrl/__pycache__/tokenization_ctrl.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a6bb5727ffb30798416c2b86e14780350f9e2fe5 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/ctrl/__pycache__/tokenization_ctrl.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/dac/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/dac/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dc4ee0109237bfc1f360a5009489d2a7f00ec83d Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/dac/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/dac/__pycache__/configuration_dac.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/dac/__pycache__/configuration_dac.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7c98d10f0776c32e7b979455943412fbf03b9dc0 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/dac/__pycache__/configuration_dac.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/dac/__pycache__/feature_extraction_dac.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/dac/__pycache__/feature_extraction_dac.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cbc0cbceff6bf1d49e210c9063be18b89e75529e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/dac/__pycache__/feature_extraction_dac.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/dac/__pycache__/modeling_dac.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/dac/__pycache__/modeling_dac.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3ebfaedfa8b8569949ffaca61836ebf8129373c3 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/dac/__pycache__/modeling_dac.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/decision_transformer/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/decision_transformer/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..afd4aee204907c4d6f622e8f0cd37ca8001f47ee Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/decision_transformer/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/decision_transformer/__pycache__/configuration_decision_transformer.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/decision_transformer/__pycache__/configuration_decision_transformer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e1bbd1961e1ca38452dbb99bef388a9062bb92c1 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/decision_transformer/__pycache__/configuration_decision_transformer.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/decision_transformer/__pycache__/modeling_decision_transformer.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/decision_transformer/__pycache__/modeling_decision_transformer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8a8173c9970c8cd02ea6bdf5bc7fb0855a1e602c Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/decision_transformer/__pycache__/modeling_decision_transformer.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/deepseek_v2/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/deepseek_v2/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..358a279d9e348b3e9f962e8a4f4b707a6a819929 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/deepseek_v2/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/deepseek_v2/__pycache__/configuration_deepseek_v2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/deepseek_v2/__pycache__/configuration_deepseek_v2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3040020b48bff6ace30e057b13a6010994fb2963 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/deepseek_v2/__pycache__/configuration_deepseek_v2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/deepseek_v2/__pycache__/modeling_deepseek_v2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/deepseek_v2/__pycache__/modeling_deepseek_v2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0671eb33860912fd46269768a94cb34197360b99 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/deepseek_v2/__pycache__/modeling_deepseek_v2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/deepseek_v2/__pycache__/modular_deepseek_v2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/deepseek_v2/__pycache__/modular_deepseek_v2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..65ae56ade0b11410093b9957c85c4e5b23347f7a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/deepseek_v2/__pycache__/modular_deepseek_v2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/depth_pro/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/depth_pro/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..38c7fe30c95634a84c189876f6d69d2c3ebc8271 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/depth_pro/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/depth_pro/__pycache__/configuration_depth_pro.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/depth_pro/__pycache__/configuration_depth_pro.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fd8a87e0b76d9f5d03b3d4a147b2b146258b65b2 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/depth_pro/__pycache__/configuration_depth_pro.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/depth_pro/__pycache__/image_processing_depth_pro.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/depth_pro/__pycache__/image_processing_depth_pro.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..591ad42e2f6e870f6f053cf150bec846aa513332 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/depth_pro/__pycache__/image_processing_depth_pro.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/depth_pro/__pycache__/image_processing_depth_pro_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/depth_pro/__pycache__/image_processing_depth_pro_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..29b13eb1ac94db45ca518f6f27eb82d9f419c8b6 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/depth_pro/__pycache__/image_processing_depth_pro_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/depth_pro/__pycache__/modeling_depth_pro.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/depth_pro/__pycache__/modeling_depth_pro.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c053c58049fdc2180f20b9a7fbe68bf6f018ca2b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/depth_pro/__pycache__/modeling_depth_pro.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/dialogpt/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/dialogpt/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f79209edaa4b52f1015413b6003b826ee2ca2815 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/dialogpt/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/dinov2/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/dinov2/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..002d7e9881f1ff3bc40090898d5ea2f78640dd91 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/dinov2/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/dinov2/__pycache__/configuration_dinov2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/dinov2/__pycache__/configuration_dinov2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..92342a62c5fdd13e3461bafa422bb1556c6d1a91 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/dinov2/__pycache__/configuration_dinov2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/dinov2/__pycache__/modeling_dinov2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/dinov2/__pycache__/modeling_dinov2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2b80a016538b15b433fd4b5c5d92de7afe6807d2 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/dinov2/__pycache__/modeling_dinov2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/dinov2/__pycache__/modeling_flax_dinov2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/dinov2/__pycache__/modeling_flax_dinov2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..81289143529671d6880ec1ab1a946d39dde84c3a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/dinov2/__pycache__/modeling_flax_dinov2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/dinov3_convnext/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/dinov3_convnext/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ee469c1be08cdc5964d260c14beae76d579bb1dc Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/dinov3_convnext/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/dinov3_convnext/__pycache__/configuration_dinov3_convnext.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/dinov3_convnext/__pycache__/configuration_dinov3_convnext.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..af060d61e530ca4c6f55d6a60774c63efd55f88c Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/dinov3_convnext/__pycache__/configuration_dinov3_convnext.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/dinov3_convnext/__pycache__/modeling_dinov3_convnext.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/dinov3_convnext/__pycache__/modeling_dinov3_convnext.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..14ce6460b121f059bc5ee832520a48b53556713b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/dinov3_convnext/__pycache__/modeling_dinov3_convnext.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/distilbert/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/distilbert/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..967fa1443ddc4abd367313303ca986cd047167b3 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/distilbert/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/distilbert/__pycache__/configuration_distilbert.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/distilbert/__pycache__/configuration_distilbert.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..af64489788e46d83f28ce70bb7f72a1ed2bbd183 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/distilbert/__pycache__/configuration_distilbert.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/distilbert/__pycache__/modeling_distilbert.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/distilbert/__pycache__/modeling_distilbert.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fc0ebeda2757ba1dcb6280aa99e149edb8a85038 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/distilbert/__pycache__/modeling_distilbert.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/distilbert/__pycache__/modeling_flax_distilbert.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/distilbert/__pycache__/modeling_flax_distilbert.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d0c5470fbb5e0ddfaf0dd39f55d1b67a276532cb Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/distilbert/__pycache__/modeling_flax_distilbert.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/distilbert/__pycache__/modeling_tf_distilbert.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/distilbert/__pycache__/modeling_tf_distilbert.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2d94f05f7f16076ffae3f51039eefe8af2ceefbe Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/distilbert/__pycache__/modeling_tf_distilbert.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/distilbert/__pycache__/tokenization_distilbert.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/distilbert/__pycache__/tokenization_distilbert.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..405c52043ec648da5087278b3360d34016ffdde6 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/distilbert/__pycache__/tokenization_distilbert.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/distilbert/__pycache__/tokenization_distilbert_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/distilbert/__pycache__/tokenization_distilbert_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..33c7a926a82ba5616088bbdfa76ec59d8013c532 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/distilbert/__pycache__/tokenization_distilbert_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/edgetam/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/edgetam/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..600c36ea16b0c2008e4175277f3856ae112da5a7 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/edgetam/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/edgetam/__pycache__/configuration_edgetam.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/edgetam/__pycache__/configuration_edgetam.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1374f59246834d859cddcfe614fccf120213378a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/edgetam/__pycache__/configuration_edgetam.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/edgetam/__pycache__/modeling_edgetam.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/edgetam/__pycache__/modeling_edgetam.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f4a529fb30f0636fd9297fbf2118b8df1abe63ab Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/edgetam/__pycache__/modeling_edgetam.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/edgetam/__pycache__/modular_edgetam.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/edgetam/__pycache__/modular_edgetam.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b23fd478afdf5046ed295b37c0115093c9205656 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/edgetam/__pycache__/modular_edgetam.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/edgetam_video/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/edgetam_video/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..72ae9b45befe78be179646e7ab4bce45309b1479 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/edgetam_video/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/edgetam_video/__pycache__/configuration_edgetam_video.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/edgetam_video/__pycache__/configuration_edgetam_video.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b1326df9a9514cc2460feff8af51ae68223a552e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/edgetam_video/__pycache__/configuration_edgetam_video.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/edgetam_video/__pycache__/modular_edgetam_video.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/edgetam_video/__pycache__/modular_edgetam_video.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..55ebfe82f7d18e9a62d45108211257ec7ed70dfd Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/edgetam_video/__pycache__/modular_edgetam_video.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/efficientloftr/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/efficientloftr/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..62a3832175f97da75a5fde96a9761845199113dc Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/efficientloftr/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/efficientloftr/__pycache__/configuration_efficientloftr.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/efficientloftr/__pycache__/configuration_efficientloftr.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..96198be409b9434524a1b9ae8e7eaf13829f0cdd Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/efficientloftr/__pycache__/configuration_efficientloftr.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/efficientloftr/__pycache__/image_processing_efficientloftr.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/efficientloftr/__pycache__/image_processing_efficientloftr.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..57aa196be1c6b37199e533344fefe3bc57e7ad89 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/efficientloftr/__pycache__/image_processing_efficientloftr.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/efficientloftr/__pycache__/image_processing_efficientloftr_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/efficientloftr/__pycache__/image_processing_efficientloftr_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f7f24f47ec75dba65aa92751f13f95a0d2de4e1f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/efficientloftr/__pycache__/image_processing_efficientloftr_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/efficientloftr/__pycache__/modeling_efficientloftr.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/efficientloftr/__pycache__/modeling_efficientloftr.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e381e1c7d2551be8deaf7e62d3a5f9ab44b9c020 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/efficientloftr/__pycache__/modeling_efficientloftr.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/encodec/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/encodec/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cb84b2937e56cd83e8c68e0cf43514cabb373000 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/encodec/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/encodec/__pycache__/configuration_encodec.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/encodec/__pycache__/configuration_encodec.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..50feb664ec1d64bbcd0e916fb385a87f0b487aba Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/encodec/__pycache__/configuration_encodec.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/encodec/__pycache__/feature_extraction_encodec.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/encodec/__pycache__/feature_extraction_encodec.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5ba0e31acf5cbd68690541a76e8050a8b49e6806 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/encodec/__pycache__/feature_extraction_encodec.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/encodec/__pycache__/modeling_encodec.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/encodec/__pycache__/modeling_encodec.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..130c39d913314295bc66b5cbdc265053b77e83b4 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/encodec/__pycache__/modeling_encodec.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/ernie4_5/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/ernie4_5/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e4a03e1204ad1a4ecc46cd4e5750b901892de27f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/ernie4_5/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/ernie4_5/__pycache__/configuration_ernie4_5.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/ernie4_5/__pycache__/configuration_ernie4_5.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..33f4c982fcf05a508d27952c58b15a9f0b211078 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/ernie4_5/__pycache__/configuration_ernie4_5.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/ernie4_5/__pycache__/modeling_ernie4_5.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/ernie4_5/__pycache__/modeling_ernie4_5.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..beae96bfdb907d46c4b7236a36aa2e4837178165 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/ernie4_5/__pycache__/modeling_ernie4_5.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/ernie4_5/__pycache__/modular_ernie4_5.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/ernie4_5/__pycache__/modular_ernie4_5.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a0f73f64cbca0110dc560f17b6ab1bc37e8fcd34 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/ernie4_5/__pycache__/modular_ernie4_5.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/flex_olmo/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/flex_olmo/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5c84e9553ac39fded378c570846399e03c08edb0 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/flex_olmo/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/flex_olmo/__pycache__/configuration_flex_olmo.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/flex_olmo/__pycache__/configuration_flex_olmo.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6c1f1462f8171cca988fe878f4401b6ae57c74b4 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/flex_olmo/__pycache__/configuration_flex_olmo.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/flex_olmo/__pycache__/modeling_flex_olmo.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/flex_olmo/__pycache__/modeling_flex_olmo.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d8da169a0f13808702af4a111cb7474efbc4e5c3 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/flex_olmo/__pycache__/modeling_flex_olmo.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/flex_olmo/__pycache__/modular_flex_olmo.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/flex_olmo/__pycache__/modular_flex_olmo.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a165ea932675f54ade7df8b4b112263532a88b3a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/flex_olmo/__pycache__/modular_flex_olmo.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/fnet/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/fnet/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e0e2c0281d86e999b5535fa22f4a38076673c919 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/fnet/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/fnet/__pycache__/configuration_fnet.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/fnet/__pycache__/configuration_fnet.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aea8737e2d3132430d3a6c6621ab277b03fdc1fd Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/fnet/__pycache__/configuration_fnet.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/fnet/__pycache__/modeling_fnet.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/fnet/__pycache__/modeling_fnet.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7505f09e316430863aa30d8e36186135ec035cf6 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/fnet/__pycache__/modeling_fnet.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/fnet/__pycache__/tokenization_fnet.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/fnet/__pycache__/tokenization_fnet.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..62b5bc3f7933ef87845d9b44ae00a3281063dc11 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/fnet/__pycache__/tokenization_fnet.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/fnet/__pycache__/tokenization_fnet_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/fnet/__pycache__/tokenization_fnet_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ce1bf18035b7c08e2165e77bce52ce122ef82845 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/fnet/__pycache__/tokenization_fnet_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/focalnet/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/focalnet/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..29a9f37121ab0d756fdfa82d638401ab3f52746f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/focalnet/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/focalnet/__pycache__/configuration_focalnet.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/focalnet/__pycache__/configuration_focalnet.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..045f034c34d8d874c6dc29287a607d26574ce672 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/focalnet/__pycache__/configuration_focalnet.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/focalnet/__pycache__/modeling_focalnet.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/focalnet/__pycache__/modeling_focalnet.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3385c8fc500fe30843b5e2ff6d52c29964103b3b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/focalnet/__pycache__/modeling_focalnet.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/fuyu/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/fuyu/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f847359640eb7466b95ab431c4a5a4ef0a51bbc1 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/fuyu/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/fuyu/__pycache__/configuration_fuyu.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/fuyu/__pycache__/configuration_fuyu.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a61d01dc329d619da45d9d36f77ebed9a3056ecc Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/fuyu/__pycache__/configuration_fuyu.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/fuyu/__pycache__/image_processing_fuyu.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/fuyu/__pycache__/image_processing_fuyu.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1ef9d0895fd63a7b4d2afc128265010ffd554521 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/fuyu/__pycache__/image_processing_fuyu.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/fuyu/__pycache__/modeling_fuyu.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/fuyu/__pycache__/modeling_fuyu.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bd58377ebd288397edd3898dcc4945576888e304 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/fuyu/__pycache__/modeling_fuyu.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/fuyu/__pycache__/processing_fuyu.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/fuyu/__pycache__/processing_fuyu.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..05e90c8f4eec2e9b0faee699901915cf65d2c9c0 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/fuyu/__pycache__/processing_fuyu.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/gemma/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/gemma/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ccd78b47e3d470eb4b6b99180f27b7b04857b5b8 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/gemma/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/gemma/__pycache__/configuration_gemma.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/gemma/__pycache__/configuration_gemma.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..21288a0d6b4e81aa03f57e3d0c885d225b927511 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/gemma/__pycache__/configuration_gemma.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/gemma/__pycache__/modeling_flax_gemma.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/gemma/__pycache__/modeling_flax_gemma.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eed38aef8adbddbe9260c5b355db5e7943d32254 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/gemma/__pycache__/modeling_flax_gemma.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/gemma/__pycache__/modeling_gemma.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/gemma/__pycache__/modeling_gemma.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d0a094b40d7cffd704e5edb299941654efc84a94 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/gemma/__pycache__/modeling_gemma.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/gemma/__pycache__/modular_gemma.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/gemma/__pycache__/modular_gemma.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dc8a464855b30395945be0f148a47e81bcc961f4 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/gemma/__pycache__/modular_gemma.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/gemma/__pycache__/tokenization_gemma.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/gemma/__pycache__/tokenization_gemma.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..69a353616ba5e87b823156549c271dbe83e8b0df Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/gemma/__pycache__/tokenization_gemma.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/gemma/__pycache__/tokenization_gemma_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/gemma/__pycache__/tokenization_gemma_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..028d037601fc5df39c80f1040bcf7fa600a829e0 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/gemma/__pycache__/tokenization_gemma_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/gemma3/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/gemma3/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..79d17757efbd618539e9c2b579caf26577c99b8b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/gemma3/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/gemma3/__pycache__/configuration_gemma3.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/gemma3/__pycache__/configuration_gemma3.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..85b4908bc3f99ea10d9eb1e910b37f3543a3e736 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/gemma3/__pycache__/configuration_gemma3.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/gemma3/__pycache__/image_processing_gemma3.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/gemma3/__pycache__/image_processing_gemma3.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..39b5e8c2ef1641192aa2d14e9b65148d3743834e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/gemma3/__pycache__/image_processing_gemma3.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/gemma3/__pycache__/image_processing_gemma3_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/gemma3/__pycache__/image_processing_gemma3_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..13a429b824103cdeff2e60b11165e32c5b5a4654 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/gemma3/__pycache__/image_processing_gemma3_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/gemma3/__pycache__/modeling_gemma3.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/gemma3/__pycache__/modeling_gemma3.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c7ae5fc0fbfb0affcce65398264d81e4cfd102ef Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/gemma3/__pycache__/modeling_gemma3.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/gemma3/__pycache__/modular_gemma3.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/gemma3/__pycache__/modular_gemma3.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0b86be6e5b67f7c159f4daed532b2dd583305acd Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/gemma3/__pycache__/modular_gemma3.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/gemma3/__pycache__/processing_gemma3.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/gemma3/__pycache__/processing_gemma3.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d3ac8af777e193f68e6e81416ac763922842cc9f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/gemma3/__pycache__/processing_gemma3.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/git/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/git/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9ffcb87131257cf9127e332188962cd7c1682ead Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/git/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/git/__pycache__/configuration_git.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/git/__pycache__/configuration_git.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9f716f06a3ee4aa304358f41170014bb3c4486c8 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/git/__pycache__/configuration_git.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/git/__pycache__/modeling_git.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/git/__pycache__/modeling_git.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..50192b1f68543f9d50b48f1e9db24dc682a1425e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/git/__pycache__/modeling_git.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/git/__pycache__/processing_git.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/git/__pycache__/processing_git.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8c8416efe87da6de3f2c358524677704f8437688 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/git/__pycache__/processing_git.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/glm/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/glm/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3348e7291a28da02b3b5e5b5920fff23e0f5c685 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/glm/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/glm/__pycache__/configuration_glm.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/glm/__pycache__/configuration_glm.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c07d1df34ebb7517386f0dbc5a6e0898f69f444f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/glm/__pycache__/configuration_glm.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/glm/__pycache__/modeling_glm.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/glm/__pycache__/modeling_glm.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1d954db1fbe360c0e183c0d54ad8ffe9cffc3c37 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/glm/__pycache__/modeling_glm.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/glm/__pycache__/modular_glm.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/glm/__pycache__/modular_glm.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9bb344ed7cb319a57c488dce7e2e7baff749d0ae Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/glm/__pycache__/modular_glm.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/glm4/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/glm4/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5e8fd2682074a697bd34312e15e0df9e2bb68bde Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/glm4/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/glm4/__pycache__/configuration_glm4.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/glm4/__pycache__/configuration_glm4.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a027b478dce77516129447c6bfa7fa3d15e96688 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/glm4/__pycache__/configuration_glm4.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/glm4/__pycache__/modeling_glm4.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/glm4/__pycache__/modeling_glm4.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6cf6aa5df2233464d16996dbbba01666828df8d7 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/glm4/__pycache__/modeling_glm4.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/glm4/__pycache__/modular_glm4.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/glm4/__pycache__/modular_glm4.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a2853404abcd0efc0f58e5cd1be6c12d15154a34 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/glm4/__pycache__/modular_glm4.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/gpt_neox/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/gpt_neox/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b7c546379820a96db2466784ea005448da85f515 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/gpt_neox/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/gpt_neox/__pycache__/configuration_gpt_neox.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/gpt_neox/__pycache__/configuration_gpt_neox.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..105c1b902e2bd4322dc2bd499b5ff050d7e2e7a4 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/gpt_neox/__pycache__/configuration_gpt_neox.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/gpt_neox/__pycache__/modeling_gpt_neox.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/gpt_neox/__pycache__/modeling_gpt_neox.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e64ecb1f150c70ff141c0b03bf0281460fc5cbce Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/gpt_neox/__pycache__/modeling_gpt_neox.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/gpt_neox/__pycache__/modular_gpt_neox.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/gpt_neox/__pycache__/modular_gpt_neox.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8121a79ea041062c62c757ee3774a322f76b64bf Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/gpt_neox/__pycache__/modular_gpt_neox.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/gpt_neox/__pycache__/tokenization_gpt_neox_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/gpt_neox/__pycache__/tokenization_gpt_neox_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4e8649967c1cc648040383adc7242e010062cd1e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/gpt_neox/__pycache__/tokenization_gpt_neox_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/gptj/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/gptj/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..53a38f8cd9b5b71dc8e27ac8cfb27330f8353859 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/gptj/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/gptj/__pycache__/configuration_gptj.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/gptj/__pycache__/configuration_gptj.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..466ca8512bf046a17735bf0fb0e474ebd6a39b82 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/gptj/__pycache__/configuration_gptj.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/gptj/__pycache__/modeling_flax_gptj.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/gptj/__pycache__/modeling_flax_gptj.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..50dd8db5c5c389471b477d3c80d3ce05dcc92f7d Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/gptj/__pycache__/modeling_flax_gptj.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/gptj/__pycache__/modeling_gptj.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/gptj/__pycache__/modeling_gptj.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..248e0b1691f4933b411f3e9d44f4fe73109a1e14 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/gptj/__pycache__/modeling_gptj.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/gptj/__pycache__/modeling_tf_gptj.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/gptj/__pycache__/modeling_tf_gptj.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..befc968840aad274ce7de87fad93f96c33f15289 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/gptj/__pycache__/modeling_tf_gptj.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/granite/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/granite/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..60eea8bfceadebe03977dc868dcee28ca3ec38ba Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/granite/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/granite/__pycache__/configuration_granite.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/granite/__pycache__/configuration_granite.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cf27ba41b61a8d550b1c7693de6ac73509540112 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/granite/__pycache__/configuration_granite.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/granite/__pycache__/modeling_granite.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/granite/__pycache__/modeling_granite.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0aea16fff25b0668620f5da914006b186f08c2bb Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/granite/__pycache__/modeling_granite.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/granite/__pycache__/modular_granite.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/granite/__pycache__/modular_granite.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c16a1b63d819b45224bea4bc339e99c70c077256 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/granite/__pycache__/modular_granite.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/granitemoe/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/granitemoe/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f338f182d5cc7f6c9e2e798a6cc8368d35168e2f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/granitemoe/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/granitemoe/__pycache__/configuration_granitemoe.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/granitemoe/__pycache__/configuration_granitemoe.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ff16f921e09f2d067d9820d7fd580c5d16ee909a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/granitemoe/__pycache__/configuration_granitemoe.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/granitemoe/__pycache__/modeling_granitemoe.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/granitemoe/__pycache__/modeling_granitemoe.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ca2c4661591f55f3c7cbea7747f157a8a0c322ee Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/granitemoe/__pycache__/modeling_granitemoe.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/grounding_dino/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/grounding_dino/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f4b76c289bc78d4724b1d68273acc2bb1052dfe6 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/grounding_dino/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/grounding_dino/__pycache__/configuration_grounding_dino.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/grounding_dino/__pycache__/configuration_grounding_dino.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..07ed28545487299b5af201e0b5a60abecdb8e01b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/grounding_dino/__pycache__/configuration_grounding_dino.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/grounding_dino/__pycache__/image_processing_grounding_dino.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/grounding_dino/__pycache__/image_processing_grounding_dino.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ad2f88a630c7769966f684cd7a536116240e82d6 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/grounding_dino/__pycache__/image_processing_grounding_dino.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/grounding_dino/__pycache__/image_processing_grounding_dino_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/grounding_dino/__pycache__/image_processing_grounding_dino_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0b822ccb515e7a4d6b587b921cf2b81e6a550edd Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/grounding_dino/__pycache__/image_processing_grounding_dino_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/grounding_dino/__pycache__/modular_grounding_dino.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/grounding_dino/__pycache__/modular_grounding_dino.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a4f4ce1aa9bb90cc41c7d8c8479923ed4cb6493c Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/grounding_dino/__pycache__/modular_grounding_dino.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/grounding_dino/__pycache__/processing_grounding_dino.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/grounding_dino/__pycache__/processing_grounding_dino.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8274e9937714d981a2a0d6a35fa04bdd5d360840 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/grounding_dino/__pycache__/processing_grounding_dino.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/hgnet_v2/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/hgnet_v2/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c4dc86af3e37f6b054e72b337024e2929ff6445f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/hgnet_v2/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/hgnet_v2/__pycache__/configuration_hgnet_v2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/hgnet_v2/__pycache__/configuration_hgnet_v2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..19e90dd0f1981f91a1f24a8b577217e6359cb695 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/hgnet_v2/__pycache__/configuration_hgnet_v2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/hgnet_v2/__pycache__/modeling_hgnet_v2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/hgnet_v2/__pycache__/modeling_hgnet_v2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..61f2c01b0cd75bbc3148f3212b19ea9862caf06e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/hgnet_v2/__pycache__/modeling_hgnet_v2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/hgnet_v2/__pycache__/modular_hgnet_v2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/hgnet_v2/__pycache__/modular_hgnet_v2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7efb88d440d2d9a0575f5c8ff969115af7034d0b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/hgnet_v2/__pycache__/modular_hgnet_v2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/hunyuan_v1_dense/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/hunyuan_v1_dense/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f8ad50c54bfb95dc490439a543f4df3e56536983 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/hunyuan_v1_dense/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/hunyuan_v1_dense/__pycache__/configuration_hunyuan_v1_dense.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/hunyuan_v1_dense/__pycache__/configuration_hunyuan_v1_dense.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..82dadc571e2aa2f9656d86ec11e3f14fea991aa0 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/hunyuan_v1_dense/__pycache__/configuration_hunyuan_v1_dense.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/hunyuan_v1_dense/__pycache__/modeling_hunyuan_v1_dense.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/hunyuan_v1_dense/__pycache__/modeling_hunyuan_v1_dense.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8b0d167f26153b228d67e766024ac4a23019e33e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/hunyuan_v1_dense/__pycache__/modeling_hunyuan_v1_dense.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/hunyuan_v1_dense/__pycache__/modular_hunyuan_v1_dense.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/hunyuan_v1_dense/__pycache__/modular_hunyuan_v1_dense.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..36dcaf48dbbf60be80b74a483370cf5dca8a26fa Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/hunyuan_v1_dense/__pycache__/modular_hunyuan_v1_dense.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/idefics2/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/idefics2/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..68d11a475aea62d772bdfaa084563c9930aa0d54 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/idefics2/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/idefics2/__pycache__/configuration_idefics2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/idefics2/__pycache__/configuration_idefics2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..61f84c3cb8299c6e601434a6398337bd47b97ab5 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/idefics2/__pycache__/configuration_idefics2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/idefics2/__pycache__/image_processing_idefics2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/idefics2/__pycache__/image_processing_idefics2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..62192f596cb90c5064193de17ccebae044e35c5d Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/idefics2/__pycache__/image_processing_idefics2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/idefics2/__pycache__/image_processing_idefics2_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/idefics2/__pycache__/image_processing_idefics2_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c780212f4d317096cc27fca78a3ac5b9c8fcbd76 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/idefics2/__pycache__/image_processing_idefics2_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/idefics2/__pycache__/modeling_idefics2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/idefics2/__pycache__/modeling_idefics2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..302349ca8919153cc838b2583474cb4978512a19 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/idefics2/__pycache__/modeling_idefics2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/idefics2/__pycache__/processing_idefics2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/idefics2/__pycache__/processing_idefics2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7b2363020bedb27fa1bb861a6736a1d4f9eb8bbd Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/idefics2/__pycache__/processing_idefics2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/internvl/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/internvl/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a0a45259cd5ac536ba5368482fbb1d68990d5fef Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/internvl/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/internvl/__pycache__/configuration_internvl.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/internvl/__pycache__/configuration_internvl.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..250246ddef4c48efee908af508280b655fa15b47 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/internvl/__pycache__/configuration_internvl.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/internvl/__pycache__/modeling_internvl.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/internvl/__pycache__/modeling_internvl.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3c014254f6b6925091f9ab776b3a63ab28c02d83 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/internvl/__pycache__/modeling_internvl.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/internvl/__pycache__/modular_internvl.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/internvl/__pycache__/modular_internvl.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..72ba8f1a964dc63bfac33bf2041ca7245621c337 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/internvl/__pycache__/modular_internvl.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/internvl/__pycache__/processing_internvl.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/internvl/__pycache__/processing_internvl.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ec7277ac3e1ace36c965fdb2979c89841ad51556 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/internvl/__pycache__/processing_internvl.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/internvl/__pycache__/video_processing_internvl.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/internvl/__pycache__/video_processing_internvl.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..355965d1b642932306435cd09826a0724dea3c28 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/internvl/__pycache__/video_processing_internvl.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/janus/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/janus/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f1d6b1d48ec16354f0bf8e4d43221a8c59fe75b9 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/janus/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/janus/__pycache__/configuration_janus.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/janus/__pycache__/configuration_janus.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ff984f4d2fb7186cbbaedbe546611e4e7ed6b617 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/janus/__pycache__/configuration_janus.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/janus/__pycache__/image_processing_janus.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/janus/__pycache__/image_processing_janus.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d30fe42d89e7762baa4ce44b5c3e63e0292db930 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/janus/__pycache__/image_processing_janus.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/janus/__pycache__/image_processing_janus_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/janus/__pycache__/image_processing_janus_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9d01c8a50d38f6df00297424a1ac97a90024de94 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/janus/__pycache__/image_processing_janus_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/janus/__pycache__/modeling_janus.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/janus/__pycache__/modeling_janus.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e72bc95b904aab6b82130e1dcae24b0c9adcb3c1 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/janus/__pycache__/modeling_janus.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/janus/__pycache__/modular_janus.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/janus/__pycache__/modular_janus.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fc512b95d5658bab3b0dcc5e5fe56fbe23abbf6b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/janus/__pycache__/modular_janus.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/janus/__pycache__/processing_janus.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/janus/__pycache__/processing_janus.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..24e4fdde99398368243961ea3366f7ed6cbc094a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/janus/__pycache__/processing_janus.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/jetmoe/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/jetmoe/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a078f229f161a31fe8cbf887023eb4b698a56c50 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/jetmoe/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/jetmoe/__pycache__/configuration_jetmoe.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/jetmoe/__pycache__/configuration_jetmoe.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..05d599cd3e184464543c89b63becf5af0f39a1a4 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/jetmoe/__pycache__/configuration_jetmoe.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/jetmoe/__pycache__/modeling_jetmoe.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/jetmoe/__pycache__/modeling_jetmoe.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..32d7ace1d99c86c52846b37d0a2c362fa38d5e42 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/jetmoe/__pycache__/modeling_jetmoe.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/kosmos2_5/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/kosmos2_5/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2e8a167ff584f49b83a7bb281606d4c5493ec90c Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/kosmos2_5/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/kosmos2_5/__pycache__/configuration_kosmos2_5.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/kosmos2_5/__pycache__/configuration_kosmos2_5.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1a37be737ba28133e9df298139458bb2c0353234 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/kosmos2_5/__pycache__/configuration_kosmos2_5.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/kosmos2_5/__pycache__/image_processing_kosmos2_5.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/kosmos2_5/__pycache__/image_processing_kosmos2_5.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0d163cdd02510b0fdb124b061a3ac28c51c44f8b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/kosmos2_5/__pycache__/image_processing_kosmos2_5.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/kosmos2_5/__pycache__/image_processing_kosmos2_5_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/kosmos2_5/__pycache__/image_processing_kosmos2_5_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..82bd08c5b121fd0872d41e8680d10a6281bf45c4 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/kosmos2_5/__pycache__/image_processing_kosmos2_5_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/kosmos2_5/__pycache__/modeling_kosmos2_5.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/kosmos2_5/__pycache__/modeling_kosmos2_5.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9e1339b281546f4f31ac7b022cddbcba0fdf8784 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/kosmos2_5/__pycache__/modeling_kosmos2_5.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/kosmos2_5/__pycache__/processing_kosmos2_5.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/kosmos2_5/__pycache__/processing_kosmos2_5.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0849caef8c69e4d4cd08a6c05e5c721fe6f5ebd1 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/kosmos2_5/__pycache__/processing_kosmos2_5.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/layoutlmv3/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/layoutlmv3/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..89df668e4ab97597f40b30f330f7190726576c51 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/layoutlmv3/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/layoutlmv3/__pycache__/configuration_layoutlmv3.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/layoutlmv3/__pycache__/configuration_layoutlmv3.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2dec82b266b06c8abfdb45c478787773c3cb86c2 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/layoutlmv3/__pycache__/configuration_layoutlmv3.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/layoutlmv3/__pycache__/feature_extraction_layoutlmv3.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/layoutlmv3/__pycache__/feature_extraction_layoutlmv3.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f8eded85f9187090f74b34228b460f2b256d7369 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/layoutlmv3/__pycache__/feature_extraction_layoutlmv3.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/layoutlmv3/__pycache__/image_processing_layoutlmv3.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/layoutlmv3/__pycache__/image_processing_layoutlmv3.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8d1f130684da1128ac4461f9b4add982704b9d5f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/layoutlmv3/__pycache__/image_processing_layoutlmv3.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/layoutlmv3/__pycache__/image_processing_layoutlmv3_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/layoutlmv3/__pycache__/image_processing_layoutlmv3_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aea9d6be3a0867429bdbd0d2762fec7b47e66028 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/layoutlmv3/__pycache__/image_processing_layoutlmv3_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/layoutlmv3/__pycache__/modeling_layoutlmv3.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/layoutlmv3/__pycache__/modeling_layoutlmv3.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dec30f9ea2faea49b9cf9d65990bef9f97f4c34b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/layoutlmv3/__pycache__/modeling_layoutlmv3.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/layoutlmv3/__pycache__/modeling_tf_layoutlmv3.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/layoutlmv3/__pycache__/modeling_tf_layoutlmv3.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6e5ff59857540077547b3aa79e1f37e5dcdcfbfa Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/layoutlmv3/__pycache__/modeling_tf_layoutlmv3.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/layoutlmv3/__pycache__/processing_layoutlmv3.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/layoutlmv3/__pycache__/processing_layoutlmv3.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f5707c05e8f0a06bf896278a45261b90b4f41ecc Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/layoutlmv3/__pycache__/processing_layoutlmv3.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/layoutlmv3/__pycache__/tokenization_layoutlmv3.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/layoutlmv3/__pycache__/tokenization_layoutlmv3.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ebfb3ce4cf347cc17f56a9273fc18b0abfe05285 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/layoutlmv3/__pycache__/tokenization_layoutlmv3.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/layoutlmv3/__pycache__/tokenization_layoutlmv3_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/layoutlmv3/__pycache__/tokenization_layoutlmv3_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f5bd980c06b959cdbaa8268870aad67296988bcf Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/layoutlmv3/__pycache__/tokenization_layoutlmv3_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/layoutxlm/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/layoutxlm/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..88433de0fdc82a252a1930523189b5c0eec37595 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/layoutxlm/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/layoutxlm/__pycache__/processing_layoutxlm.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/layoutxlm/__pycache__/processing_layoutxlm.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ea1c2b752b9e1010e7b287719328b7f9c8fa64c7 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/layoutxlm/__pycache__/processing_layoutxlm.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/layoutxlm/__pycache__/tokenization_layoutxlm.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/layoutxlm/__pycache__/tokenization_layoutxlm.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5e98852ef29edc836cf822defab123e845822f8f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/layoutxlm/__pycache__/tokenization_layoutxlm.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/layoutxlm/__pycache__/tokenization_layoutxlm_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/layoutxlm/__pycache__/tokenization_layoutxlm_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c33924b854b3273c032f42fdb707bfb0680ad75a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/layoutxlm/__pycache__/tokenization_layoutxlm_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/lfm2/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/lfm2/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bcd9bf0f2ffa854aaa03ab69d0737b00016a6dfb Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/lfm2/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/lfm2/__pycache__/configuration_lfm2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/lfm2/__pycache__/configuration_lfm2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..896a6a836609569aa354be77a1faa2783da87783 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/lfm2/__pycache__/configuration_lfm2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/lfm2/__pycache__/modeling_lfm2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/lfm2/__pycache__/modeling_lfm2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f51632ace01930285aba6073350dce0cb7798bef Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/lfm2/__pycache__/modeling_lfm2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/lfm2/__pycache__/modular_lfm2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/lfm2/__pycache__/modular_lfm2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1a338bbaf405450658e2cb60f59e252d6dd2dd03 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/lfm2/__pycache__/modular_lfm2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/lilt/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/lilt/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cd5c7d57800fd16cfbf44e268957a2eba9b15290 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/lilt/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/lilt/__pycache__/configuration_lilt.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/lilt/__pycache__/configuration_lilt.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eab85bbf6a248f59d25e891439b1ed37a8177dc4 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/lilt/__pycache__/configuration_lilt.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/lilt/__pycache__/modeling_lilt.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/lilt/__pycache__/modeling_lilt.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..88de42b0299fc0112f18bb860676882ab46ce018 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/lilt/__pycache__/modeling_lilt.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/llama/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/llama/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..346143dde29d66f345a1747abc3b010b5201bf88 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/llama/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/llama/__pycache__/configuration_llama.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/llama/__pycache__/configuration_llama.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bb670a685606068933c0a5ef30b22b1c21d2015e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/llama/__pycache__/configuration_llama.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/llama/__pycache__/modeling_flax_llama.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/llama/__pycache__/modeling_flax_llama.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1de27c2c579aaca31594d85ca1f7a38f40faa623 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/llama/__pycache__/modeling_flax_llama.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/llama/__pycache__/modeling_llama.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/llama/__pycache__/modeling_llama.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6e930d15c431b4a0e1aee8db5d8564660f410531 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/llama/__pycache__/modeling_llama.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/llama/__pycache__/tokenization_llama.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/llama/__pycache__/tokenization_llama.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7a89d5eeaf936a5cd79c7843deaf25b1d46133bd Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/llama/__pycache__/tokenization_llama.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/llama/__pycache__/tokenization_llama_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/llama/__pycache__/tokenization_llama_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cdb6173de14a973021c7ba2ec370754892fbd76d Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/llama/__pycache__/tokenization_llama_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/llava/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/llava/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0c9fda46bb558fd5bbec24d1df001fe1ed265903 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/llava/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/llava/__pycache__/configuration_llava.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/llava/__pycache__/configuration_llava.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c21d5c908cc304036e8fb6c22b7d9f65d4fcae93 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/llava/__pycache__/configuration_llava.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/llava/__pycache__/image_processing_llava.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/llava/__pycache__/image_processing_llava.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..77b5fe9676bcc48a2d1ba1a307a76a596e40d7d6 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/llava/__pycache__/image_processing_llava.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/llava/__pycache__/image_processing_llava_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/llava/__pycache__/image_processing_llava_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3d4dc34fbd5dfc835f0d45cafa3f46b3d57f211b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/llava/__pycache__/image_processing_llava_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/llava/__pycache__/modeling_llava.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/llava/__pycache__/modeling_llava.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f659e86c0ed0defa0b502052e0519e56df0cb31b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/llava/__pycache__/modeling_llava.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/llava/__pycache__/processing_llava.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/llava/__pycache__/processing_llava.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..814d4fcdc4cebda54c9ef49bc35d3e85d8cacc9e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/llava/__pycache__/processing_llava.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/llava_next/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/llava_next/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..46240ffdb85112a6f5ddc6c846c8d8f4cf67919c Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/llava_next/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/llava_next/__pycache__/configuration_llava_next.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/llava_next/__pycache__/configuration_llava_next.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3a21d466d9d4b5baa9d93422ecd1474567330175 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/llava_next/__pycache__/configuration_llava_next.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/llava_next/__pycache__/image_processing_llava_next.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/llava_next/__pycache__/image_processing_llava_next.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..78645d08310a013ac47efd7c3b29fae490dd5c5b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/llava_next/__pycache__/image_processing_llava_next.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/llava_next/__pycache__/image_processing_llava_next_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/llava_next/__pycache__/image_processing_llava_next_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c81fd4474691d34245e36fd407c20f549a15032a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/llava_next/__pycache__/image_processing_llava_next_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/llava_next/__pycache__/modeling_llava_next.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/llava_next/__pycache__/modeling_llava_next.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a224f4a2ddbc3c88726bacc79956e7e1d2ef9670 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/llava_next/__pycache__/modeling_llava_next.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/llava_next/__pycache__/processing_llava_next.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/llava_next/__pycache__/processing_llava_next.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..21b8797e9848ab11971439965f07319e04111565 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/llava_next/__pycache__/processing_llava_next.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/llava_next_video/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/llava_next_video/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9943a3824c951f80d5695f04d3ba51b054c65412 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/llava_next_video/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/llava_next_video/__pycache__/configuration_llava_next_video.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/llava_next_video/__pycache__/configuration_llava_next_video.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1e49169c9bdedeb92a986356e68e705508c7d82e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/llava_next_video/__pycache__/configuration_llava_next_video.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/llava_next_video/__pycache__/image_processing_llava_next_video.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/llava_next_video/__pycache__/image_processing_llava_next_video.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9e9f3889df23835bb9859441dd073ad30c69189f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/llava_next_video/__pycache__/image_processing_llava_next_video.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/llava_next_video/__pycache__/modeling_llava_next_video.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/llava_next_video/__pycache__/modeling_llava_next_video.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1e5c61c6d0e1f6c81f099821bab016537dc178b1 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/llava_next_video/__pycache__/modeling_llava_next_video.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/llava_next_video/__pycache__/modular_llava_next_video.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/llava_next_video/__pycache__/modular_llava_next_video.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5212deb5eae053378643b8a221200a8ab065a9f9 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/llava_next_video/__pycache__/modular_llava_next_video.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/llava_next_video/__pycache__/processing_llava_next_video.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/llava_next_video/__pycache__/processing_llava_next_video.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fe84435dffb9bddaddebfdcbea5bf5e32f6c0443 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/llava_next_video/__pycache__/processing_llava_next_video.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/llava_next_video/__pycache__/video_processing_llava_next_video.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/llava_next_video/__pycache__/video_processing_llava_next_video.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b05521e4d1a7501ee4d75bd27c4123a5e9176648 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/llava_next_video/__pycache__/video_processing_llava_next_video.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/longcat_flash/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/longcat_flash/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..99358c0c1125f40d1eefff8fac60c01ff63e7863 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/longcat_flash/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/longcat_flash/__pycache__/configuration_longcat_flash.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/longcat_flash/__pycache__/configuration_longcat_flash.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f7203750f43c22b12a8c705b52a41c334dd924e1 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/longcat_flash/__pycache__/configuration_longcat_flash.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/longcat_flash/__pycache__/modeling_longcat_flash.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/longcat_flash/__pycache__/modeling_longcat_flash.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ac3e9f3a6667055170de2e85e67655b988180f17 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/longcat_flash/__pycache__/modeling_longcat_flash.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/longcat_flash/__pycache__/modular_longcat_flash.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/longcat_flash/__pycache__/modular_longcat_flash.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d43878415e8fe1e856cd18ddaff580b29af6b48b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/longcat_flash/__pycache__/modular_longcat_flash.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/longt5/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/longt5/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..af06ebf07eeeeb0d09e7247f6d61fce3315a8de7 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/longt5/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/longt5/__pycache__/configuration_longt5.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/longt5/__pycache__/configuration_longt5.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5f601e397020f3e4cfb5d1a6983155f93706bcce Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/longt5/__pycache__/configuration_longt5.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/m2m_100/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/m2m_100/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3620f7c9ce1fe5dcabd1b275a4f341f75eaf2cef Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/m2m_100/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/m2m_100/__pycache__/configuration_m2m_100.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/m2m_100/__pycache__/configuration_m2m_100.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..136532860384dfd617c11a708a110b36594b9b6f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/m2m_100/__pycache__/configuration_m2m_100.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/m2m_100/__pycache__/modeling_m2m_100.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/m2m_100/__pycache__/modeling_m2m_100.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6f715607ab87d40cb02f8d68d5d04e225cd9955a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/m2m_100/__pycache__/modeling_m2m_100.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/m2m_100/__pycache__/tokenization_m2m_100.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/m2m_100/__pycache__/tokenization_m2m_100.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fdad94a5b54d23331edb15d609cfe95ef6026364 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/m2m_100/__pycache__/tokenization_m2m_100.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mamba2/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mamba2/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1b5764dbdf48cf46dd298792ded4693908d1622b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mamba2/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mamba2/__pycache__/configuration_mamba2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mamba2/__pycache__/configuration_mamba2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..894e9da8e3a47f1b2447acad66a8e74443038cc3 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mamba2/__pycache__/configuration_mamba2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mamba2/__pycache__/modeling_mamba2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mamba2/__pycache__/modeling_mamba2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..31bbf1ee6c7fe9d17281b4ac55ff358fed789a0c Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mamba2/__pycache__/modeling_mamba2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/maskformer/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/maskformer/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..560492520c351fead318cce9744a5deaa82fe118 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/maskformer/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/maskformer/__pycache__/configuration_maskformer.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/maskformer/__pycache__/configuration_maskformer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..99f4e9c89e691e6cb7f518f6da4c552aff94670f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/maskformer/__pycache__/configuration_maskformer.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/maskformer/__pycache__/configuration_maskformer_swin.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/maskformer/__pycache__/configuration_maskformer_swin.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..85f934771efc6aa18255087a0e9efdc12667b303 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/maskformer/__pycache__/configuration_maskformer_swin.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/maskformer/__pycache__/feature_extraction_maskformer.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/maskformer/__pycache__/feature_extraction_maskformer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a1615f6161f341d686719fa087928bf526fa8e18 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/maskformer/__pycache__/feature_extraction_maskformer.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/maskformer/__pycache__/image_processing_maskformer.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/maskformer/__pycache__/image_processing_maskformer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bb2532efbb59ef92dafb8b55befa247a93199fb4 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/maskformer/__pycache__/image_processing_maskformer.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/maskformer/__pycache__/image_processing_maskformer_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/maskformer/__pycache__/image_processing_maskformer_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f5babe7fc48f98335db601d6466624b9a5d7f757 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/maskformer/__pycache__/image_processing_maskformer_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/maskformer/__pycache__/modeling_maskformer.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/maskformer/__pycache__/modeling_maskformer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4c1c1741e6e967fcd5de596f2200644166742169 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/maskformer/__pycache__/modeling_maskformer.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/maskformer/__pycache__/modeling_maskformer_swin.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/maskformer/__pycache__/modeling_maskformer_swin.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d5246f68b85bdd6ae0f27adc63e005f419551b7e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/maskformer/__pycache__/modeling_maskformer_swin.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mbart/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mbart/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7b66bd8465408b55ef6ff517ba8ad7452060f793 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mbart/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mbart/__pycache__/configuration_mbart.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mbart/__pycache__/configuration_mbart.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..82389a866a99f9f58aad56c20d33adfe986c644d Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mbart/__pycache__/configuration_mbart.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mbart/__pycache__/modeling_flax_mbart.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mbart/__pycache__/modeling_flax_mbart.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a40e9373fb01d4ac7f42e10001739159c91e38a2 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mbart/__pycache__/modeling_flax_mbart.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mbart/__pycache__/modeling_mbart.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mbart/__pycache__/modeling_mbart.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d4f94b4c7ddf62fb8f67dcbf2609e559af28ba8d Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mbart/__pycache__/modeling_mbart.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mbart/__pycache__/modeling_tf_mbart.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mbart/__pycache__/modeling_tf_mbart.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..98392b83105640a29714e6fd10105003efe716fc Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mbart/__pycache__/modeling_tf_mbart.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mbart/__pycache__/tokenization_mbart.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mbart/__pycache__/tokenization_mbart.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..91c49664c01b8c884299f511467baa564a220607 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mbart/__pycache__/tokenization_mbart.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mbart/__pycache__/tokenization_mbart_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mbart/__pycache__/tokenization_mbart_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1aa0acf5f47293b9b2dde77f26649b3962ba11e6 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mbart/__pycache__/tokenization_mbart_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mgp_str/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mgp_str/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cde3ba29719140f0d554bbd0c95ae8c4dc914a08 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mgp_str/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mgp_str/__pycache__/configuration_mgp_str.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mgp_str/__pycache__/configuration_mgp_str.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..42a3605e5ce89ecb14e1a92cdb75dcbb6d776ecf Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mgp_str/__pycache__/configuration_mgp_str.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mgp_str/__pycache__/modeling_mgp_str.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mgp_str/__pycache__/modeling_mgp_str.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..35992c54ba7ac78f0277f0fe7e6cc8bb5f2f9e90 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mgp_str/__pycache__/modeling_mgp_str.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mgp_str/__pycache__/processing_mgp_str.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mgp_str/__pycache__/processing_mgp_str.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..74a3b25b75646709abdf95ea543ab1edfc7a325f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mgp_str/__pycache__/processing_mgp_str.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mgp_str/__pycache__/tokenization_mgp_str.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mgp_str/__pycache__/tokenization_mgp_str.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d16788454255d3833828f37704d0a15a3d26eddf Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mgp_str/__pycache__/tokenization_mgp_str.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/minimax/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/minimax/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..25636751605f60dbd0de396ce513da82dea63481 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/minimax/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/minimax/__pycache__/configuration_minimax.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/minimax/__pycache__/configuration_minimax.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8183218dc3a62399f09703803f952676db62abd3 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/minimax/__pycache__/configuration_minimax.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/minimax/__pycache__/modeling_minimax.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/minimax/__pycache__/modeling_minimax.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6574b0c749d200508eac1ac0374532d13f09c2d1 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/minimax/__pycache__/modeling_minimax.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/minimax/__pycache__/modular_minimax.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/minimax/__pycache__/modular_minimax.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0a5abdf303a5a573834835b0d0f4a091d4da914a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/minimax/__pycache__/modular_minimax.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/ministral/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/ministral/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6117423a6b98d4bca56aa92e9d6642f8d5ce4c6c Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/ministral/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/ministral/__pycache__/configuration_ministral.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/ministral/__pycache__/configuration_ministral.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1794aeb0a5cb71d2c6100026b1eea7f3a9e2184f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/ministral/__pycache__/configuration_ministral.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/ministral/__pycache__/modeling_ministral.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/ministral/__pycache__/modeling_ministral.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..40bf7f2e078206a3a3890c6a4d1453bcd612a26b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/ministral/__pycache__/modeling_ministral.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/ministral/__pycache__/modular_ministral.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/ministral/__pycache__/modular_ministral.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4895e778d8b8c9203e31c8764be55212d9d0093c Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/ministral/__pycache__/modular_ministral.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mistral/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mistral/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eba5f55b652ef981d9ec37d5ccedfc1ff8feb788 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mistral/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mistral/__pycache__/configuration_mistral.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mistral/__pycache__/configuration_mistral.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1a5f8d0609176234516346c9292a57da89adb6b3 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mistral/__pycache__/configuration_mistral.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mistral/__pycache__/modeling_flax_mistral.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mistral/__pycache__/modeling_flax_mistral.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d5defc8e4c81b226cfec3df7aa03d9b935bf263f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mistral/__pycache__/modeling_flax_mistral.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mistral/__pycache__/modeling_mistral.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mistral/__pycache__/modeling_mistral.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e10e9e5a28f0cf8492553bb30932848e7ee6613e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mistral/__pycache__/modeling_mistral.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mistral/__pycache__/modeling_tf_mistral.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mistral/__pycache__/modeling_tf_mistral.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c5560a299eed5207decf28ea87c9c964a6212edf Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mistral/__pycache__/modeling_tf_mistral.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mistral/__pycache__/modular_mistral.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mistral/__pycache__/modular_mistral.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..56ca6c9507d5688098aea5bd245ee9990bea666c Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mistral/__pycache__/modular_mistral.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mllama/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mllama/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d41b2f6280bc9ed74633dd4fd1597ce8c57d9d50 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mllama/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mllama/__pycache__/configuration_mllama.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mllama/__pycache__/configuration_mllama.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b50d4a55b3499131e7b0802210c22d20761de40b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mllama/__pycache__/configuration_mllama.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mllama/__pycache__/image_processing_mllama.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mllama/__pycache__/image_processing_mllama.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..87a0dcdeb08c62c527499975c9ea4c4a09070968 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mllama/__pycache__/image_processing_mllama.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mllama/__pycache__/modeling_mllama.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mllama/__pycache__/modeling_mllama.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e1e4c3d497effe76ab57710ce8a0ea18747365cf Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mllama/__pycache__/modeling_mllama.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mllama/__pycache__/processing_mllama.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mllama/__pycache__/processing_mllama.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4217166d1bc3c9650e44456bbec66b632639dd4a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mllama/__pycache__/processing_mllama.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mobilenet_v2/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mobilenet_v2/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2bffc59bde46185ad0e4d0167bed344469fe5c19 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mobilenet_v2/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mobilenet_v2/__pycache__/configuration_mobilenet_v2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mobilenet_v2/__pycache__/configuration_mobilenet_v2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..869f6e7029b354aab9e0cd6c2898336dd97997f3 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mobilenet_v2/__pycache__/configuration_mobilenet_v2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mobilenet_v2/__pycache__/feature_extraction_mobilenet_v2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mobilenet_v2/__pycache__/feature_extraction_mobilenet_v2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3e760c0f4842a2bb5dbd0353923ec745a502f813 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mobilenet_v2/__pycache__/feature_extraction_mobilenet_v2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mobilenet_v2/__pycache__/image_processing_mobilenet_v2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mobilenet_v2/__pycache__/image_processing_mobilenet_v2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cb014b01fba922f283eb4b598bb5ff386bd7d582 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mobilenet_v2/__pycache__/image_processing_mobilenet_v2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mobilenet_v2/__pycache__/image_processing_mobilenet_v2_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mobilenet_v2/__pycache__/image_processing_mobilenet_v2_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5765454c2b31cd57f0baaa7c730b720b93b96a0f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mobilenet_v2/__pycache__/image_processing_mobilenet_v2_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/mobilenet_v2/__pycache__/modeling_mobilenet_v2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/mobilenet_v2/__pycache__/modeling_mobilenet_v2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3de8e5a7efdc4a20f6f523277e892319f3b670e6 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/mobilenet_v2/__pycache__/modeling_mobilenet_v2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/modernbert_decoder/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/modernbert_decoder/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..450cea1cf6d651ca3932a8a78ff47ad8afee258e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/modernbert_decoder/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/modernbert_decoder/__pycache__/configuration_modernbert_decoder.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/modernbert_decoder/__pycache__/configuration_modernbert_decoder.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8e981467eb68227d61d150b5e018b3b8c0b73dd5 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/modernbert_decoder/__pycache__/configuration_modernbert_decoder.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/modernbert_decoder/__pycache__/modeling_modernbert_decoder.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/modernbert_decoder/__pycache__/modeling_modernbert_decoder.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e71be04ec4e4bfb80ac677c7920ab14cc4c33de6 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/modernbert_decoder/__pycache__/modeling_modernbert_decoder.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/modernbert_decoder/__pycache__/modular_modernbert_decoder.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/modernbert_decoder/__pycache__/modular_modernbert_decoder.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..12288012bd827e7d64113b33d5f98485647f9c14 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/modernbert_decoder/__pycache__/modular_modernbert_decoder.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/myt5/__pycache__/tokenization_myt5.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/myt5/__pycache__/tokenization_myt5.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8c6f899a138460a7e4128da6678d0313be29ed22 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/myt5/__pycache__/tokenization_myt5.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/olmo2/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/olmo2/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1fa2a088ed10acb066dd8c8a382513f0923d8dcc Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/olmo2/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/olmo2/__pycache__/configuration_olmo2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/olmo2/__pycache__/configuration_olmo2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6cf3f0e76dfa3cf8c2d5d953bfa9de2ec574438e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/olmo2/__pycache__/configuration_olmo2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/olmo2/__pycache__/modeling_olmo2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/olmo2/__pycache__/modeling_olmo2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..327f15758748ce0d29097d76a45696fa8969493a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/olmo2/__pycache__/modeling_olmo2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/olmo2/__pycache__/modular_olmo2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/olmo2/__pycache__/modular_olmo2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..35638770975e15ad12f7b6002714f36f26beff32 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/olmo2/__pycache__/modular_olmo2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/openai/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/openai/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1f9e576033ddeb1161dfa9d67f8709c480837c97 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/openai/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/openai/__pycache__/configuration_openai.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/openai/__pycache__/configuration_openai.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..598a09bc0166f946bfb6bc7c164091d830affca2 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/openai/__pycache__/configuration_openai.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/openai/__pycache__/modeling_openai.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/openai/__pycache__/modeling_openai.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0ceac88001e741d8159fd81f1608360853155769 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/openai/__pycache__/modeling_openai.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/openai/__pycache__/modeling_tf_openai.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/openai/__pycache__/modeling_tf_openai.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a95767466573802ce2887151b79874e923183984 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/openai/__pycache__/modeling_tf_openai.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/openai/__pycache__/tokenization_openai.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/openai/__pycache__/tokenization_openai.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1b9d68afab17719fccf6abf528c4e94698032833 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/openai/__pycache__/tokenization_openai.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/openai/__pycache__/tokenization_openai_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/openai/__pycache__/tokenization_openai_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..18d62e54a15f3e48f956f49cca0ed6c71f11ff16 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/openai/__pycache__/tokenization_openai_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/ovis2/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/ovis2/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..40126f56d4cc902d2cbebc196cd0150fa9091c8d Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/ovis2/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/ovis2/__pycache__/configuration_ovis2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/ovis2/__pycache__/configuration_ovis2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..51aa8236948b7ecc569a578713c3881ba181f7bf Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/ovis2/__pycache__/configuration_ovis2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/ovis2/__pycache__/image_processing_ovis2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/ovis2/__pycache__/image_processing_ovis2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ca2e949d5ba9a884ca213624b6313d3a64c83286 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/ovis2/__pycache__/image_processing_ovis2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/ovis2/__pycache__/image_processing_ovis2_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/ovis2/__pycache__/image_processing_ovis2_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b053a0c3b394bf2496ebb52af6a186aae57adf0d Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/ovis2/__pycache__/image_processing_ovis2_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/ovis2/__pycache__/modeling_ovis2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/ovis2/__pycache__/modeling_ovis2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..66e6f1f773debc12ccf512b295bc529fbc53dd51 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/ovis2/__pycache__/modeling_ovis2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/ovis2/__pycache__/modular_ovis2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/ovis2/__pycache__/modular_ovis2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a3e64818a33ce2a90592df1743fa64357d49768a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/ovis2/__pycache__/modular_ovis2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/ovis2/__pycache__/processing_ovis2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/ovis2/__pycache__/processing_ovis2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eeb53efdfee0dfef022618afd92f1d83e03ad7b5 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/ovis2/__pycache__/processing_ovis2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/parakeet/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/parakeet/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..200985a65a857375af86e834e1cbc1364939732e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/parakeet/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/parakeet/__pycache__/configuration_parakeet.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/parakeet/__pycache__/configuration_parakeet.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..77763d8cd8bbeec5ddccfe6dbd8f825eedefb66e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/parakeet/__pycache__/configuration_parakeet.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/parakeet/__pycache__/feature_extraction_parakeet.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/parakeet/__pycache__/feature_extraction_parakeet.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..311725a7cfc3c14c91e58b2e3b4ee74389a4365d Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/parakeet/__pycache__/feature_extraction_parakeet.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/parakeet/__pycache__/modeling_parakeet.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/parakeet/__pycache__/modeling_parakeet.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b40823e37e004c82e94b1e6f95d733139a24dae3 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/parakeet/__pycache__/modeling_parakeet.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/parakeet/__pycache__/modular_parakeet.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/parakeet/__pycache__/modular_parakeet.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..154ee54ad7e26d510a55a726e0b394ef706d72d8 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/parakeet/__pycache__/modular_parakeet.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/parakeet/__pycache__/processing_parakeet.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/parakeet/__pycache__/processing_parakeet.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..74b828bf338e30569cb2c2dacbf1820d63ba838a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/parakeet/__pycache__/processing_parakeet.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/parakeet/__pycache__/tokenization_parakeet_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/parakeet/__pycache__/tokenization_parakeet_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e6eb31c43d8bca66f1a80d277cfb482e0c867eb9 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/parakeet/__pycache__/tokenization_parakeet_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/pegasus_x/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/pegasus_x/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e33104ff5616a82ca07b802f62b99913e7119813 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/pegasus_x/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/pegasus_x/__pycache__/configuration_pegasus_x.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/pegasus_x/__pycache__/configuration_pegasus_x.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bec4c75032bea64d8ff53b463dad752a0505a829 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/pegasus_x/__pycache__/configuration_pegasus_x.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/pegasus_x/__pycache__/modeling_pegasus_x.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/pegasus_x/__pycache__/modeling_pegasus_x.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..52358da814a890a676648beda52aa35a25b8718f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/pegasus_x/__pycache__/modeling_pegasus_x.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/perception_lm/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/perception_lm/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..10f4a6f23100009c716a1eb77444654c398f519a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/perception_lm/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/perception_lm/__pycache__/configuration_perception_lm.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/perception_lm/__pycache__/configuration_perception_lm.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d8a609a6b3b95c09d81725dae2d7405028e4b9ad Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/perception_lm/__pycache__/configuration_perception_lm.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/perception_lm/__pycache__/image_processing_perception_lm_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/perception_lm/__pycache__/image_processing_perception_lm_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bb5e1cda6b59590af223b2b6db30b0cb57359365 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/perception_lm/__pycache__/image_processing_perception_lm_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/perception_lm/__pycache__/modeling_perception_lm.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/perception_lm/__pycache__/modeling_perception_lm.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..40933bcd5bb322ed2c2af06cfbfa3b1fbc2e7e36 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/perception_lm/__pycache__/modeling_perception_lm.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/perception_lm/__pycache__/modular_perception_lm.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/perception_lm/__pycache__/modular_perception_lm.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8aadb69d86c84c8eab07d8a24b662a0db3a6e00a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/perception_lm/__pycache__/modular_perception_lm.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/perception_lm/__pycache__/processing_perception_lm.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/perception_lm/__pycache__/processing_perception_lm.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..617449e52c3d0617dd9dd14dc0201acadd5e80f8 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/perception_lm/__pycache__/processing_perception_lm.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/perception_lm/__pycache__/video_processing_perception_lm.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/perception_lm/__pycache__/video_processing_perception_lm.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..90e07d43d49efabaa62153edd0773221816f0f90 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/perception_lm/__pycache__/video_processing_perception_lm.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/persimmon/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/persimmon/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..76bf8d6f33a442ab430c27cd679f04ef82316cdd Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/persimmon/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/persimmon/__pycache__/configuration_persimmon.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/persimmon/__pycache__/configuration_persimmon.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a4e22da9af5d6b8d8d74b173bd4bceee65a31b7a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/persimmon/__pycache__/configuration_persimmon.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/persimmon/__pycache__/modeling_persimmon.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/persimmon/__pycache__/modeling_persimmon.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1594d1ee5c3fb1748cf7371f213ac5471cfbc073 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/persimmon/__pycache__/modeling_persimmon.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/phi4_multimodal/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/phi4_multimodal/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b7fea1eb83f8ffdecd2008410ccd56d9befb2ff2 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/phi4_multimodal/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/phi4_multimodal/__pycache__/configuration_phi4_multimodal.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/phi4_multimodal/__pycache__/configuration_phi4_multimodal.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ba0a6ad1b7f5121e9c47d758c74d780a7c697ef7 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/phi4_multimodal/__pycache__/configuration_phi4_multimodal.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/phi4_multimodal/__pycache__/feature_extraction_phi4_multimodal.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/phi4_multimodal/__pycache__/feature_extraction_phi4_multimodal.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cb119bc16dd1515e86d9fceee5dde1a96e51f612 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/phi4_multimodal/__pycache__/feature_extraction_phi4_multimodal.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/phi4_multimodal/__pycache__/image_processing_phi4_multimodal_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/phi4_multimodal/__pycache__/image_processing_phi4_multimodal_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ae6bff58a13b3ba54102518393b574e89d28e353 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/phi4_multimodal/__pycache__/image_processing_phi4_multimodal_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/phi4_multimodal/__pycache__/modular_phi4_multimodal.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/phi4_multimodal/__pycache__/modular_phi4_multimodal.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a7a480e3d4757b4b6f1ef92c744360da6c45c5dc Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/phi4_multimodal/__pycache__/modular_phi4_multimodal.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/phi4_multimodal/__pycache__/processing_phi4_multimodal.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/phi4_multimodal/__pycache__/processing_phi4_multimodal.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ecfabd083259fe3daeaa740c0a41889aa4c0c9f6 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/phi4_multimodal/__pycache__/processing_phi4_multimodal.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/phobert/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/phobert/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b9349d1246b067b48311d41f131af62544477033 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/phobert/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/phobert/__pycache__/tokenization_phobert.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/phobert/__pycache__/tokenization_phobert.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..585549bc7f3dc78a55e3514c16d1bba36551b29a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/phobert/__pycache__/tokenization_phobert.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/pvt/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/pvt/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cc369cb2b8b55e5d9631bde39a17a279e83bf71e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/pvt/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/pvt/__pycache__/configuration_pvt.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/pvt/__pycache__/configuration_pvt.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..db7580cbe9f800dcbfc5e493b671fc3f3b3b4257 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/pvt/__pycache__/configuration_pvt.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/pvt/__pycache__/image_processing_pvt.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/pvt/__pycache__/image_processing_pvt.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6934a79f1beb79c24c392d064d84e474d0d2960b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/pvt/__pycache__/image_processing_pvt.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/pvt/__pycache__/image_processing_pvt_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/pvt/__pycache__/image_processing_pvt_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..62378644300fd700532b7d5a8d95f649e6a146e1 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/pvt/__pycache__/image_processing_pvt_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/pvt/__pycache__/modeling_pvt.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/pvt/__pycache__/modeling_pvt.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b84685dcc24678b9133161cb9831c228905af506 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/pvt/__pycache__/modeling_pvt.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/pvt_v2/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/pvt_v2/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0f01cab9d1a78a69882623f11a10c7e996e6b406 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/pvt_v2/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/pvt_v2/__pycache__/configuration_pvt_v2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/pvt_v2/__pycache__/configuration_pvt_v2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..84496d3298cf2a310697a03ab5721c4116a7664f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/pvt_v2/__pycache__/configuration_pvt_v2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/pvt_v2/__pycache__/modeling_pvt_v2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/pvt_v2/__pycache__/modeling_pvt_v2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3f46c92e196e2045fc5103001f7103750e045ecc Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/pvt_v2/__pycache__/modeling_pvt_v2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/qwen2_moe/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/qwen2_moe/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b522948670f815cefdda574677b8d95ed33584dc Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/qwen2_moe/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/qwen2_moe/__pycache__/configuration_qwen2_moe.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/qwen2_moe/__pycache__/configuration_qwen2_moe.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fb0f6ada77d21c64602749311c2b5e0911cc233e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/qwen2_moe/__pycache__/configuration_qwen2_moe.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/qwen2_moe/__pycache__/modeling_qwen2_moe.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/qwen2_moe/__pycache__/modeling_qwen2_moe.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a0da7fa876558efd5443f34046c24b0956a9476a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/qwen2_moe/__pycache__/modeling_qwen2_moe.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/regnet/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/regnet/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1b9638bb6581fe601809e84529b6d1a1f46ad573 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/regnet/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/regnet/__pycache__/configuration_regnet.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/regnet/__pycache__/configuration_regnet.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5c876dce252e55ce7c2cba5c65be10db7c0690a4 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/regnet/__pycache__/configuration_regnet.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/regnet/__pycache__/modeling_flax_regnet.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/regnet/__pycache__/modeling_flax_regnet.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7aa2d7449c0f6e640ff28d6618b4abffb8e4510f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/regnet/__pycache__/modeling_flax_regnet.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/regnet/__pycache__/modeling_regnet.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/regnet/__pycache__/modeling_regnet.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ffb5a461e57d1abe74c6f6e5dd25130af1d30159 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/regnet/__pycache__/modeling_regnet.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/regnet/__pycache__/modeling_tf_regnet.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/regnet/__pycache__/modeling_tf_regnet.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..791ea85c8374fa2f68588536248b778fb981bb28 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/regnet/__pycache__/modeling_tf_regnet.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/roc_bert/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/roc_bert/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..98d98cfd96e6bdfe61becf080f2328b21d96c9fe Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/roc_bert/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/roc_bert/__pycache__/configuration_roc_bert.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/roc_bert/__pycache__/configuration_roc_bert.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..11c9df9067c41f0e116e208736c5f812bf3ed9cc Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/roc_bert/__pycache__/configuration_roc_bert.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/roc_bert/__pycache__/modeling_roc_bert.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/roc_bert/__pycache__/modeling_roc_bert.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..733479c38b953717e1b911f6ac828c18eddee05e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/roc_bert/__pycache__/modeling_roc_bert.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/roc_bert/__pycache__/tokenization_roc_bert.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/roc_bert/__pycache__/tokenization_roc_bert.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6dfe8da04019b3f5635e189fb3a4ffcf19dd0b0d Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/roc_bert/__pycache__/tokenization_roc_bert.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/roformer/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/roformer/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..575170eed68a4102ffa26b5b079755011df15d3b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/roformer/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/roformer/__pycache__/configuration_roformer.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/roformer/__pycache__/configuration_roformer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a8bfc6b7d8165ca61f8e9af7181aaa055bb27c94 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/roformer/__pycache__/configuration_roformer.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/roformer/__pycache__/modeling_flax_roformer.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/roformer/__pycache__/modeling_flax_roformer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f4c00ecae9bd78c7f0edddd6525e2caba22f2107 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/roformer/__pycache__/modeling_flax_roformer.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/roformer/__pycache__/modeling_roformer.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/roformer/__pycache__/modeling_roformer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e7ae3bcb97f193dab8a1bc7356dff6a151330475 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/roformer/__pycache__/modeling_roformer.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/roformer/__pycache__/modeling_tf_roformer.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/roformer/__pycache__/modeling_tf_roformer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1939e2b7491ae5cf9c25d94911b34687307a26bb Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/roformer/__pycache__/modeling_tf_roformer.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/roformer/__pycache__/tokenization_roformer.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/roformer/__pycache__/tokenization_roformer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..23ee9f36b322c52b905b06ba22c2fe7daef9c10c Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/roformer/__pycache__/tokenization_roformer.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/roformer/__pycache__/tokenization_roformer_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/roformer/__pycache__/tokenization_roformer_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7d85bc826a0dabba8a5f4a575fda27545dd7698e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/roformer/__pycache__/tokenization_roformer_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/roformer/__pycache__/tokenization_utils.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/roformer/__pycache__/tokenization_utils.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f6b6b21d44e172a43594950980a2ac66fc411957 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/roformer/__pycache__/tokenization_utils.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/rt_detr/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/rt_detr/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5d997633ab5afb2845b451bbc1bd73c6d9c85999 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/rt_detr/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/rt_detr/__pycache__/configuration_rt_detr.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/rt_detr/__pycache__/configuration_rt_detr.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cb418ec00d09a263d5be38dd63c3e192d9e6a7e7 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/rt_detr/__pycache__/configuration_rt_detr.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/rt_detr/__pycache__/configuration_rt_detr_resnet.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/rt_detr/__pycache__/configuration_rt_detr_resnet.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4c4bc72a3510b333233f557ff4a520671282068f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/rt_detr/__pycache__/configuration_rt_detr_resnet.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/rt_detr/__pycache__/image_processing_rt_detr.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/rt_detr/__pycache__/image_processing_rt_detr.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5bda541d60a8ef2128434a66157250f2b7e83503 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/rt_detr/__pycache__/image_processing_rt_detr.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/rt_detr/__pycache__/image_processing_rt_detr_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/rt_detr/__pycache__/image_processing_rt_detr_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f25e5c7c9373ed5966ebec2d647d0937a3c55935 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/rt_detr/__pycache__/image_processing_rt_detr_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/rt_detr/__pycache__/modeling_rt_detr_resnet.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/rt_detr/__pycache__/modeling_rt_detr_resnet.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6dc9756020bdba2ecd56d8caadba0ed0ba956905 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/rt_detr/__pycache__/modeling_rt_detr_resnet.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/rt_detr/__pycache__/modular_rt_detr.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/rt_detr/__pycache__/modular_rt_detr.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..64b5e257d2069abecc83e9107b1cfccc52444460 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/rt_detr/__pycache__/modular_rt_detr.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/rwkv/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/rwkv/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..167a7931f8c50d26659c6709922a8ff12955487d Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/rwkv/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/rwkv/__pycache__/configuration_rwkv.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/rwkv/__pycache__/configuration_rwkv.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c65f88ba3f227ddf16469b1696b7abd132ca9513 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/rwkv/__pycache__/configuration_rwkv.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/rwkv/__pycache__/modeling_rwkv.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/rwkv/__pycache__/modeling_rwkv.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b84fb3019e804884069757c19096b4b214fd0659 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/rwkv/__pycache__/modeling_rwkv.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/seamless_m4t_v2/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/seamless_m4t_v2/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cce85dec0f2a8df88e066f1ab9a770db348fafb2 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/seamless_m4t_v2/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/seamless_m4t_v2/__pycache__/configuration_seamless_m4t_v2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/seamless_m4t_v2/__pycache__/configuration_seamless_m4t_v2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3dd8851a712d18a141b60fd09bad1d051416ca50 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/seamless_m4t_v2/__pycache__/configuration_seamless_m4t_v2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/seed_oss/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/seed_oss/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5ce3839df3214eac16ad37407c4aa06ddfd510d0 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/seed_oss/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/seed_oss/__pycache__/configuration_seed_oss.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/seed_oss/__pycache__/configuration_seed_oss.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..512217f4180e390bd7a420326986bbececd7b6d8 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/seed_oss/__pycache__/configuration_seed_oss.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/seed_oss/__pycache__/modeling_seed_oss.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/seed_oss/__pycache__/modeling_seed_oss.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6a362559f2ed1cf531e5b7581517582aade922f5 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/seed_oss/__pycache__/modeling_seed_oss.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/seed_oss/__pycache__/modular_seed_oss.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/seed_oss/__pycache__/modular_seed_oss.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..32ecfbc84ad2132dc64cb8ec18ead08f6c4488ad Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/seed_oss/__pycache__/modular_seed_oss.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/segformer/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/segformer/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a3a4fbb6412698def37934d70cf9d3a9b9ef335e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/segformer/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/segformer/__pycache__/configuration_segformer.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/segformer/__pycache__/configuration_segformer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5253a92e46d1cbfe57df316b2c2c6d4968f5c146 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/segformer/__pycache__/configuration_segformer.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/segformer/__pycache__/feature_extraction_segformer.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/segformer/__pycache__/feature_extraction_segformer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4e2b53d782d1b9ecc84b319ca47d55def8d398bf Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/segformer/__pycache__/feature_extraction_segformer.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/segformer/__pycache__/image_processing_segformer.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/segformer/__pycache__/image_processing_segformer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a12cd373235465a57e3b8717fae55068d9ec614b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/segformer/__pycache__/image_processing_segformer.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/segformer/__pycache__/image_processing_segformer_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/segformer/__pycache__/image_processing_segformer_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f5a3ce3eee8d19613d4c592aec8f5ff91bd60861 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/segformer/__pycache__/image_processing_segformer_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/segformer/__pycache__/modeling_segformer.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/segformer/__pycache__/modeling_segformer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4c7cfbe80aad3b7b82ca9a3427b3db0196cef9e1 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/segformer/__pycache__/modeling_segformer.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/segformer/__pycache__/modeling_tf_segformer.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/segformer/__pycache__/modeling_tf_segformer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1c37a103c39a8aceebdd82c581178b6efb45a17c Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/segformer/__pycache__/modeling_tf_segformer.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/segformer/__pycache__/modular_segformer.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/segformer/__pycache__/modular_segformer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c030651c4d1a7da4fca96e7e2841e119ce4e3678 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/segformer/__pycache__/modular_segformer.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/smolvlm/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/smolvlm/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1c56a25910ff96804adc0cd1cb08aaf602d95ddd Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/smolvlm/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/smolvlm/__pycache__/configuration_smolvlm.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/smolvlm/__pycache__/configuration_smolvlm.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9cb34a50f0694b45cfa502a3045cfde96a2dcbc1 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/smolvlm/__pycache__/configuration_smolvlm.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/smolvlm/__pycache__/image_processing_smolvlm.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/smolvlm/__pycache__/image_processing_smolvlm.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b714cacd6e1075546ef72ca28e6883a83e62b805 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/smolvlm/__pycache__/image_processing_smolvlm.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/smolvlm/__pycache__/image_processing_smolvlm_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/smolvlm/__pycache__/image_processing_smolvlm_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0916ded07768e2a0dacf3577020eb0576f59d942 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/smolvlm/__pycache__/image_processing_smolvlm_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/smolvlm/__pycache__/modeling_smolvlm.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/smolvlm/__pycache__/modeling_smolvlm.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6288a1a2db82bc050a7ac6b9ab4e160b04b91f51 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/smolvlm/__pycache__/modeling_smolvlm.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/smolvlm/__pycache__/modular_smolvlm.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/smolvlm/__pycache__/modular_smolvlm.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b049c11777ac58455fe74c61cb00ba8d130e9c5c Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/smolvlm/__pycache__/modular_smolvlm.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/smolvlm/__pycache__/processing_smolvlm.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/smolvlm/__pycache__/processing_smolvlm.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..91ad4e02843112f553b2405803f459b20163d03c Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/smolvlm/__pycache__/processing_smolvlm.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/smolvlm/__pycache__/video_processing_smolvlm.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/smolvlm/__pycache__/video_processing_smolvlm.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1a45c51767d8d56e32a977b79850fb58760ae0d4 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/smolvlm/__pycache__/video_processing_smolvlm.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/swin2sr/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/swin2sr/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..335a3d87bb2be35cb713aef5834be4f2e6cb4cc7 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/swin2sr/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/swin2sr/__pycache__/configuration_swin2sr.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/swin2sr/__pycache__/configuration_swin2sr.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e7237c1cb840aff46db7da9ee77c851800e828d0 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/swin2sr/__pycache__/configuration_swin2sr.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/swin2sr/__pycache__/image_processing_swin2sr.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/swin2sr/__pycache__/image_processing_swin2sr.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ca35edb0632ffd8305018188d81da1df4065e16f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/swin2sr/__pycache__/image_processing_swin2sr.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/swin2sr/__pycache__/image_processing_swin2sr_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/swin2sr/__pycache__/image_processing_swin2sr_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5c6218cd5761cb53670e694b72a5a65e05695961 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/swin2sr/__pycache__/image_processing_swin2sr_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/swin2sr/__pycache__/modeling_swin2sr.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/swin2sr/__pycache__/modeling_swin2sr.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fc77100f88df40b5b338a0ced1fe566bcdfd7066 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/swin2sr/__pycache__/modeling_swin2sr.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/t5gemma/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/t5gemma/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..07cae366a10c5d2f7c47e449fde52385f00e725a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/t5gemma/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/t5gemma/__pycache__/configuration_t5gemma.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/t5gemma/__pycache__/configuration_t5gemma.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..96b3aff34db14c5f2c62d1cc671de54d02eecd88 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/t5gemma/__pycache__/configuration_t5gemma.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/t5gemma/__pycache__/modeling_t5gemma.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/t5gemma/__pycache__/modeling_t5gemma.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a2c6dc21bebaecb0410e56663a3762f23d267577 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/t5gemma/__pycache__/modeling_t5gemma.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/t5gemma/__pycache__/modular_t5gemma.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/t5gemma/__pycache__/modular_t5gemma.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1ccb38528a06891319f54981db597d5c27cab52d Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/t5gemma/__pycache__/modular_t5gemma.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/textnet/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/textnet/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f53ba54dcbf5c2b6ebdb57d8119120fd8135d5cb Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/textnet/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/textnet/__pycache__/configuration_textnet.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/textnet/__pycache__/configuration_textnet.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ec5641e0e62b0af565204d1b6a3dc3c9573a9c27 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/textnet/__pycache__/configuration_textnet.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/textnet/__pycache__/image_processing_textnet.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/textnet/__pycache__/image_processing_textnet.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..20f34c5f9d3acf3fc532146d5056bcd649ed71bc Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/textnet/__pycache__/image_processing_textnet.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/textnet/__pycache__/image_processing_textnet_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/textnet/__pycache__/image_processing_textnet_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b583f4d179d464434019b2ab50240112fe57ba40 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/textnet/__pycache__/image_processing_textnet_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/textnet/__pycache__/modeling_textnet.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/textnet/__pycache__/modeling_textnet.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..03139ce1f153b9fff486e32866b7b44e9b9562af Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/textnet/__pycache__/modeling_textnet.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/timesfm/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/timesfm/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..66bf57c1c91c139d3fd1cbc0776273e26f3b1e24 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/timesfm/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/timesfm/__pycache__/configuration_timesfm.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/timesfm/__pycache__/configuration_timesfm.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d6622110682f07b091ebff7208c44c4f437667f6 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/timesfm/__pycache__/configuration_timesfm.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/timesfm/__pycache__/modeling_timesfm.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/timesfm/__pycache__/modeling_timesfm.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..db01a145e84368c2972808c082901f864793cc40 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/timesfm/__pycache__/modeling_timesfm.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/timesfm/__pycache__/modular_timesfm.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/timesfm/__pycache__/modular_timesfm.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fbdb553c2659aa52344c8355a26491af63f4e7e4 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/timesfm/__pycache__/modular_timesfm.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/timesformer/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/timesformer/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..27ec620efff22073719931c7d6f023a7913a2336 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/timesformer/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/timesformer/__pycache__/configuration_timesformer.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/timesformer/__pycache__/configuration_timesformer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a8bf0ce519a6d8f1c25c09894acf15a73978fec6 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/timesformer/__pycache__/configuration_timesformer.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/timesformer/__pycache__/modeling_timesformer.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/timesformer/__pycache__/modeling_timesformer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ee522f0adf29daccd2da027c6a837db44c1383ae Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/timesformer/__pycache__/modeling_timesformer.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/unispeech/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/unispeech/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fbcb17d0432f10ffc7af5aee31c574be13e64b39 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/unispeech/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/unispeech/__pycache__/configuration_unispeech.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/unispeech/__pycache__/configuration_unispeech.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3374f47235e0022927a6b93fcc0919caa5463099 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/unispeech/__pycache__/configuration_unispeech.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/unispeech/__pycache__/modeling_unispeech.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/unispeech/__pycache__/modeling_unispeech.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e806f4353376df17a7a5aee22a24fa058ada79af Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/unispeech/__pycache__/modeling_unispeech.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/unispeech/__pycache__/modular_unispeech.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/unispeech/__pycache__/modular_unispeech.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..90bb9e4a5492f3b71f4430dbe810f53d876074c4 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/unispeech/__pycache__/modular_unispeech.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/unispeech_sat/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/unispeech_sat/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8e66d98213c6c984359b448a20d2a4902a589b94 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/unispeech_sat/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/unispeech_sat/__pycache__/configuration_unispeech_sat.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/unispeech_sat/__pycache__/configuration_unispeech_sat.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f9df742309880940de0f270b00c9ae68ed8db720 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/unispeech_sat/__pycache__/configuration_unispeech_sat.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/unispeech_sat/__pycache__/modeling_unispeech_sat.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/unispeech_sat/__pycache__/modeling_unispeech_sat.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a28ce16f6e12819d64754e33e0aef2a2e1523f0b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/unispeech_sat/__pycache__/modeling_unispeech_sat.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/unispeech_sat/__pycache__/modular_unispeech_sat.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/unispeech_sat/__pycache__/modular_unispeech_sat.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b560d08cf7ca1625f768eaafa4b146da309c5771 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/unispeech_sat/__pycache__/modular_unispeech_sat.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/upernet/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/upernet/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..076f30529cff19bc52535f7eb67bcd3ed55e1ae1 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/upernet/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/upernet/__pycache__/configuration_upernet.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/upernet/__pycache__/configuration_upernet.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..702c38a4933ef9e3ad95855f4435da8717c37887 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/upernet/__pycache__/configuration_upernet.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/upernet/__pycache__/modeling_upernet.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/upernet/__pycache__/modeling_upernet.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d43cfbedd37c7a37213b76281c438981fcf7f468 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/upernet/__pycache__/modeling_upernet.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/vipllava/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/vipllava/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0701b46faef68a4ee56fb5a431b7b27b9bf2b9d5 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/vipllava/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/vipllava/__pycache__/configuration_vipllava.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/vipllava/__pycache__/configuration_vipllava.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..33f37ec294faa09ebb82bc586b99bfa7bd003ac6 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/vipllava/__pycache__/configuration_vipllava.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/vipllava/__pycache__/modeling_vipllava.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/vipllava/__pycache__/modeling_vipllava.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c2a01a4a71d30745da97371e2481d1b4daf9c17b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/vipllava/__pycache__/modeling_vipllava.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/vipllava/__pycache__/modular_vipllava.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/vipllava/__pycache__/modular_vipllava.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..39ff92f4cf7d1cad6793a4ad2fde21d00b623797 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/vipllava/__pycache__/modular_vipllava.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/vit/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/vit/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ddf137a8165606a6429a8a69252090ec5cdf7a2a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/vit/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/vit/__pycache__/configuration_vit.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/vit/__pycache__/configuration_vit.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e4d98eba8c470d2e71a3a16b3dcbf43bf362e06d Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/vit/__pycache__/configuration_vit.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/vit/__pycache__/feature_extraction_vit.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/vit/__pycache__/feature_extraction_vit.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..42947947c17b959429a54f265e8acf3eee140740 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/vit/__pycache__/feature_extraction_vit.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/vit/__pycache__/image_processing_vit.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/vit/__pycache__/image_processing_vit.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..30760e4173337eda4a400606cc03f6fc56d3a3e3 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/vit/__pycache__/image_processing_vit.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/vit/__pycache__/image_processing_vit_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/vit/__pycache__/image_processing_vit_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a2926e998753df5e0eeec92eda02057d5207fb93 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/vit/__pycache__/image_processing_vit_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/vit/__pycache__/modeling_flax_vit.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/vit/__pycache__/modeling_flax_vit.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0985a84e960954cfe472f1e5cf1639c8adc7d4fd Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/vit/__pycache__/modeling_flax_vit.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/vit/__pycache__/modeling_tf_vit.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/vit/__pycache__/modeling_tf_vit.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4d7f518fec4c0c8921711bddd4b22a3951dbe2b5 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/vit/__pycache__/modeling_tf_vit.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/vit/__pycache__/modeling_vit.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/vit/__pycache__/modeling_vit.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..acb0471a7a70cbc3faa75e9dab24b2f7e2a507ce Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/vit/__pycache__/modeling_vit.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/vit_msn/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/vit_msn/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ed97cd100d57a9d111db1f01471025fed8c15bba Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/vit_msn/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/vit_msn/__pycache__/configuration_vit_msn.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/vit_msn/__pycache__/configuration_vit_msn.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..adbf5267da92269318045148e149d94af18a555a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/vit_msn/__pycache__/configuration_vit_msn.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/vit_msn/__pycache__/modeling_vit_msn.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/vit_msn/__pycache__/modeling_vit_msn.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1dbe8c124501a24e0488b661a6298654c325d161 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/vit_msn/__pycache__/modeling_vit_msn.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/vitmatte/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/vitmatte/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..29ef10b9e58420a16362cd69a83673d3509e7a01 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/vitmatte/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/vitmatte/__pycache__/configuration_vitmatte.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/vitmatte/__pycache__/configuration_vitmatte.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..36cc425c1db908a8b249084516ab94ef57576558 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/vitmatte/__pycache__/configuration_vitmatte.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/vitmatte/__pycache__/image_processing_vitmatte.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/vitmatte/__pycache__/image_processing_vitmatte.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9d7673998c41a15709ef1200e2bdced8f27c9682 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/vitmatte/__pycache__/image_processing_vitmatte.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/vitmatte/__pycache__/image_processing_vitmatte_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/vitmatte/__pycache__/image_processing_vitmatte_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3edc5bbeb01dfb71adc5f2018d0f7af46494250e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/vitmatte/__pycache__/image_processing_vitmatte_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/vitmatte/__pycache__/modeling_vitmatte.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/vitmatte/__pycache__/modeling_vitmatte.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7413e628277eba22b1490842ab530266f475987f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/vitmatte/__pycache__/modeling_vitmatte.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/vitpose/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/vitpose/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..016f7e55ffe9e8f2ce788064c3d4bd7fd58396ee Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/vitpose/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/vitpose/__pycache__/configuration_vitpose.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/vitpose/__pycache__/configuration_vitpose.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7490b2fc3a4a5b1c90e0b29068c3a5aae6082869 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/vitpose/__pycache__/configuration_vitpose.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/vitpose/__pycache__/image_processing_vitpose.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/vitpose/__pycache__/image_processing_vitpose.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8445b14082708ceaf85245c3683b23ccf3c4b26f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/vitpose/__pycache__/image_processing_vitpose.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/vitpose/__pycache__/modeling_vitpose.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/vitpose/__pycache__/modeling_vitpose.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d1bbf9401afe5da8d4be8371c75ee46b747246be Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/vitpose/__pycache__/modeling_vitpose.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/vits/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/vits/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..83f00d0b6583adc33c654c9f803edf4c76af44ae Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/vits/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/vits/__pycache__/configuration_vits.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/vits/__pycache__/configuration_vits.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..59b8de87d6ac3002f98ab284aae1258774ed2e51 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/vits/__pycache__/configuration_vits.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/vits/__pycache__/modeling_vits.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/vits/__pycache__/modeling_vits.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6aad403c7290f69111a337999359d9c67824f1e9 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/vits/__pycache__/modeling_vits.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/vits/__pycache__/tokenization_vits.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/vits/__pycache__/tokenization_vits.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a2f4e0f1466d10cbe376ae8ceedcfc5378988690 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/vits/__pycache__/tokenization_vits.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/voxtral/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/voxtral/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7246a276572ef258ac2ffa7800f7c6db01e855de Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/voxtral/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/voxtral/__pycache__/configuration_voxtral.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/voxtral/__pycache__/configuration_voxtral.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..42c921e1d19472b570d2cbf88a18d07397eb8539 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/voxtral/__pycache__/configuration_voxtral.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/voxtral/__pycache__/modeling_voxtral.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/voxtral/__pycache__/modeling_voxtral.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9637eb8e2592b1ac68fabf48c5f1fb1d13bc0c3c Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/voxtral/__pycache__/modeling_voxtral.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/voxtral/__pycache__/modular_voxtral.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/voxtral/__pycache__/modular_voxtral.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fe7325a65edbf828a4c0b7c770a87828ad2ce86e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/voxtral/__pycache__/modular_voxtral.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/voxtral/__pycache__/processing_voxtral.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/voxtral/__pycache__/processing_voxtral.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5716d1110d0a6e7cd935775c41551e54d0f969a1 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/voxtral/__pycache__/processing_voxtral.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/wav2vec2_bert/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/wav2vec2_bert/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5da488376e588d13c802cf14e6e4889782780924 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/wav2vec2_bert/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/wav2vec2_bert/__pycache__/configuration_wav2vec2_bert.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/wav2vec2_bert/__pycache__/configuration_wav2vec2_bert.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..24e72826b92680c977bc19ee76a4d09006351810 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/wav2vec2_bert/__pycache__/configuration_wav2vec2_bert.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/wav2vec2_bert/__pycache__/modeling_wav2vec2_bert.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/wav2vec2_bert/__pycache__/modeling_wav2vec2_bert.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b85729a2cd8622ec38ccd16a1d0ad338af11e53b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/wav2vec2_bert/__pycache__/modeling_wav2vec2_bert.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/wav2vec2_bert/__pycache__/modular_wav2vec2_bert.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/wav2vec2_bert/__pycache__/modular_wav2vec2_bert.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8ee1f049c2c83bbd71bd0f94d5bcb758fe5690cb Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/wav2vec2_bert/__pycache__/modular_wav2vec2_bert.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/wav2vec2_bert/__pycache__/processing_wav2vec2_bert.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/wav2vec2_bert/__pycache__/processing_wav2vec2_bert.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b7034621498cd84a6b42cf3ee0bbe7830dfbbc54 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/wav2vec2_bert/__pycache__/processing_wav2vec2_bert.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/wav2vec2_conformer/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/wav2vec2_conformer/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..316f87b4bf462aa9bb6f27cf9a2637d5a62d704d Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/wav2vec2_conformer/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/wav2vec2_conformer/__pycache__/configuration_wav2vec2_conformer.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/wav2vec2_conformer/__pycache__/configuration_wav2vec2_conformer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7dcc8c443980684e74e567322e625dfe7e64cfdf Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/wav2vec2_conformer/__pycache__/configuration_wav2vec2_conformer.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/wav2vec2_conformer/__pycache__/modeling_wav2vec2_conformer.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/wav2vec2_conformer/__pycache__/modeling_wav2vec2_conformer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b89f9d8ee917d708875414afb95ebc129d2843ab Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/wav2vec2_conformer/__pycache__/modeling_wav2vec2_conformer.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/wav2vec2_conformer/__pycache__/modular_wav2vec2_conformer.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/wav2vec2_conformer/__pycache__/modular_wav2vec2_conformer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fd9dd88ccd8fbc998dc751bd46bdb6486cec840e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/wav2vec2_conformer/__pycache__/modular_wav2vec2_conformer.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/xlm_roberta_xl/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/xlm_roberta_xl/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7033220e5881cffcdc655aa9531d58ebb12a4b40 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/xlm_roberta_xl/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/xlm_roberta_xl/__pycache__/configuration_xlm_roberta_xl.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/xlm_roberta_xl/__pycache__/configuration_xlm_roberta_xl.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ef65c4d972545b0042cb53e0b766c66934f4cd4b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/xlm_roberta_xl/__pycache__/configuration_xlm_roberta_xl.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/xlm_roberta_xl/__pycache__/modeling_xlm_roberta_xl.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/xlm_roberta_xl/__pycache__/modeling_xlm_roberta_xl.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..580882b760e3522f66540df7990e436d7a637de2 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/xlm_roberta_xl/__pycache__/modeling_xlm_roberta_xl.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/yolos/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/yolos/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..95e3b671f782d13a7533ffb88f6be8483520e4dd Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/yolos/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/yolos/__pycache__/configuration_yolos.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/yolos/__pycache__/configuration_yolos.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bf35983cef80837349348031d2c590bb7c635398 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/yolos/__pycache__/configuration_yolos.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/yolos/__pycache__/feature_extraction_yolos.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/yolos/__pycache__/feature_extraction_yolos.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9e79a0f946c0ede2cc38e56faea374fc65a8ad66 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/yolos/__pycache__/feature_extraction_yolos.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/yolos/__pycache__/image_processing_yolos.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/yolos/__pycache__/image_processing_yolos.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ef7a2fd1293715dbe429bcc8e88731d14c97f2e6 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/yolos/__pycache__/image_processing_yolos.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/yolos/__pycache__/image_processing_yolos_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/yolos/__pycache__/image_processing_yolos_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8d73529a69e54da0d24f831d89042c60a61a6555 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/yolos/__pycache__/image_processing_yolos_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/yolos/__pycache__/modeling_yolos.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/yolos/__pycache__/modeling_yolos.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..683fea74529254aa7141ea8cb338896effbd64ae Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/yolos/__pycache__/modeling_yolos.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/yolos/__pycache__/modular_yolos.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/yolos/__pycache__/modular_yolos.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ad6b2d48118a4797bc07f1d0779ea5a0ff7c519f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/yolos/__pycache__/modular_yolos.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/zamba/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/zamba/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4d34787ecede4dda2a39ef427848a4f691d2713f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/zamba/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/zamba/__pycache__/configuration_zamba.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/zamba/__pycache__/configuration_zamba.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..008d3487183a87df2b79248332c2960122161c4c Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/zamba/__pycache__/configuration_zamba.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/zamba/__pycache__/modeling_zamba.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/zamba/__pycache__/modeling_zamba.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1f07daf484b2bde58234042ead81b7c5f276e13a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/zamba/__pycache__/modeling_zamba.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/zoedepth/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/zoedepth/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ae8ca0d6c7cf0f596a102d069f97fea047b3d9b4 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/zoedepth/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/zoedepth/__pycache__/configuration_zoedepth.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/zoedepth/__pycache__/configuration_zoedepth.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c5d2d2273584b10462289f44c1b7ca45bc3d4e99 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/zoedepth/__pycache__/configuration_zoedepth.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/zoedepth/__pycache__/image_processing_zoedepth.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/zoedepth/__pycache__/image_processing_zoedepth.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8fa747e958524c884b2a60422593634bb74a34a8 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/zoedepth/__pycache__/image_processing_zoedepth.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/zoedepth/__pycache__/image_processing_zoedepth_fast.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/zoedepth/__pycache__/image_processing_zoedepth_fast.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7112a4b72004fb8f66ab31e0772e9b9e32d04fe5 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/zoedepth/__pycache__/image_processing_zoedepth_fast.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/models/zoedepth/__pycache__/modeling_zoedepth.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/models/zoedepth/__pycache__/modeling_zoedepth.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8b399798138fad1ac184dbb08668c181bbfb23b2 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/models/zoedepth/__pycache__/modeling_zoedepth.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/onnx/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/onnx/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c6d140be7b1ca95839e3e78cd7254dfd52182e40 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/onnx/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/onnx/__pycache__/__main__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/onnx/__pycache__/__main__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cb187d4e7c91fb74b1cce0232d86da2d7cbfc4cf Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/onnx/__pycache__/__main__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/onnx/__pycache__/config.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/onnx/__pycache__/config.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..593ba1ad77a12fcb420c9e92f64f30a3f1b927c1 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/onnx/__pycache__/config.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/onnx/__pycache__/convert.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/onnx/__pycache__/convert.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..087c79cb3c60b5180561592e6aeb5c0bb0251655 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/onnx/__pycache__/convert.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/onnx/__pycache__/features.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/onnx/__pycache__/features.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..852ad57326857878a0f8c3a6fd4af735f4839dda Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/onnx/__pycache__/features.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/onnx/__pycache__/utils.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/onnx/__pycache__/utils.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2c4f111119683cac5e3f220d9245ddc02ffdc96e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/onnx/__pycache__/utils.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..957d81b1914a838d762a1d9e56a58cafbd5d9514 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/audio_classification.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/audio_classification.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f03afd670d1283b4bc5b73dfd5033d91f0b91941 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/audio_classification.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/audio_utils.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/audio_utils.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f4cd7a24d449648ea0d869d437535c95643d8b1a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/audio_utils.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/automatic_speech_recognition.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/automatic_speech_recognition.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8a9b858396ff264d0847929a215e6224e55dd79a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/automatic_speech_recognition.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/base.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/base.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fc906a32498be63df8d9d581da2af9896de70c51 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/base.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/depth_estimation.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/depth_estimation.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4d7178f854701be780b6b357a4d9ffe00fcd0b6d Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/depth_estimation.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/document_question_answering.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/document_question_answering.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a3b1472ee689017a7108748ee2f3b15fed096de7 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/document_question_answering.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/feature_extraction.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/feature_extraction.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..630a8585601a2520e37510b319786fdc71168b81 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/feature_extraction.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/fill_mask.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/fill_mask.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9c720220ea4acd1a17be91da3bde653afb631780 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/fill_mask.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/image_classification.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/image_classification.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..77809ecaad1a9c22f33b0ffd4f28e6fe6a298afd Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/image_classification.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/image_feature_extraction.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/image_feature_extraction.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e50f10931612c02ba9683d2f95e413812fded12a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/image_feature_extraction.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/image_segmentation.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/image_segmentation.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9835f3874e6be6363ee207962dfa99ef0d2ab395 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/image_segmentation.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/image_text_to_text.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/image_text_to_text.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a3a8d0dca2da53900740bec791b8240592e217af Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/image_text_to_text.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/image_to_image.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/image_to_image.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..24ff0483f39cb6f4454e7617cf7566b1debe874d Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/image_to_image.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/image_to_text.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/image_to_text.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e584c5fa374f53266212cb7eb85a96b518bb8669 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/image_to_text.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/keypoint_matching.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/keypoint_matching.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0dad30c3905c98ba84aca4859d1ed58e8e2a134c Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/keypoint_matching.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/mask_generation.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/mask_generation.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..196a59a04b99818bf6c354a7801bfe9d2b4985b8 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/mask_generation.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/object_detection.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/object_detection.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bfdd4aed2026be6f4ea34b5fb743636e2e4c27d5 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/object_detection.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/pt_utils.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/pt_utils.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e00a5e3ac43c8603390f49ae9d3c029e5fbc01e0 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/pt_utils.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/question_answering.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/question_answering.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e1a88351ebd6d80ae2660dd13ff0a988e253d9fb Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/question_answering.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/table_question_answering.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/table_question_answering.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0dc2ace434ed15614f434acaf6ad0d0312d266c3 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/table_question_answering.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/text2text_generation.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/text2text_generation.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fd548c31fd1e0d2989706ae500773d075ed2636b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/text2text_generation.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/text_classification.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/text_classification.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2b26a38dfffe9063f0440a422f950938af3a5c43 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/text_classification.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/text_generation.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/text_generation.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8e8dec3e5b3f37d8e241ff1108e6e8325e5fb2a7 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/text_generation.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/text_to_audio.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/text_to_audio.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5f63f86b184ea14934cb5f41f1c78e09768bfc2b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/text_to_audio.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/token_classification.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/token_classification.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a8269cd7816d1e6b7bea2cf7480ec443bbbf4842 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/token_classification.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/video_classification.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/video_classification.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1e93c431bc06895ff787bd9f6fc2b7d191ef9722 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/video_classification.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/visual_question_answering.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/visual_question_answering.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..94a3ced600a08cd33a57dba9c5cf841d08517cb4 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/visual_question_answering.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/zero_shot_audio_classification.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/zero_shot_audio_classification.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b42773ff8016d82a75db7032352f00b8cf2cf41d Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/zero_shot_audio_classification.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/zero_shot_classification.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/zero_shot_classification.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..168fab5ec620c762c88b1dd19dbd0913b3cabda6 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/zero_shot_classification.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/zero_shot_image_classification.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/zero_shot_image_classification.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9ad4e5e7796489e002830cdbd672853f75c0d0cd Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/zero_shot_image_classification.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/zero_shot_object_detection.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/zero_shot_object_detection.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..08bec498499202b39cc915afbd89e6c5ea80c7bd Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/pipelines/__pycache__/zero_shot_object_detection.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4a6591e55c9a5da805d8d5f08d40427fc49b132b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/auto.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/auto.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..62599bb6039462efc6d80a9fb1da8efa3a1112b5 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/auto.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/base.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/base.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d6421d2926a08dcaf28e75ab6a092322a3c9539c Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/base.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_aqlm.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_aqlm.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..40bc64671e2b0a7040b5258517696960b71bc7c8 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_aqlm.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_auto_round.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_auto_round.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..430be97a5839a7c9bc14600c4cc9b4f425272045 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_auto_round.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_awq.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_awq.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2db4aa0719908e0ca9c52b92063a885f45ba9fad Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_awq.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_bitnet.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_bitnet.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6c89fc42e2f7fa8c3baa06a6fa33f97259895350 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_bitnet.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_bnb_4bit.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_bnb_4bit.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..42646be705a603be7ffc2c2f7e6b3023c8f535bc Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_bnb_4bit.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_bnb_8bit.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_bnb_8bit.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4b30012c7992241b8cf102af61a7264ff1d370d2 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_bnb_8bit.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_compressed_tensors.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_compressed_tensors.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..897435b91947a0835f6af0ef33891655d139fb18 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_compressed_tensors.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_eetq.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_eetq.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c918bac59ef2b140ec7a0f540dc3f6805ecfe70e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_eetq.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_fbgemm_fp8.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_fbgemm_fp8.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c5d098662bdb77037f0189e205cbb7e9310d6e12 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_fbgemm_fp8.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_finegrained_fp8.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_finegrained_fp8.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..74174bc89bc8bb5cce3bdbbae8948a7f415acd45 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_finegrained_fp8.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_fp_quant.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_fp_quant.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..52a2601ecda6d40ba4abc2fb7d986688db766dd1 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_fp_quant.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_gptq.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_gptq.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..112f060a27871f3e70c0321a9a1e575b70d32035 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_gptq.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_higgs.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_higgs.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a9e74adf451a270f5c3917472ac074b7d74bb5fb Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_higgs.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_hqq.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_hqq.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9387dfff20b925995f326267808a24efe94667e0 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_hqq.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_mxfp4.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_mxfp4.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f93e53e9c05b2e1868a2e7064f22fa63dc859972 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_mxfp4.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_quanto.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_quanto.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6ea26a8d48ea1e2216479c0cb895fde37f364f4e Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_quanto.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_quark.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_quark.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6dc6b1150525b0bda86737280dfefd6a5beb1ceb Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_quark.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_spqr.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_spqr.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..66a5f3ec471d7831dfc584f84321e43ff8ca1d9f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_spqr.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_torchao.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_torchao.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..76962f29aee515cdfe8ee5d56f1b9ef7f7e32a14 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_torchao.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_vptq.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_vptq.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f7de8dcfe4f5d3e0169182aa88fd5e37e8aa82cf Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizer_vptq.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizers_utils.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizers_utils.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2afee24a34444be51f1eb5369eaa3ef166bac615 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/quantizers/__pycache__/quantizers_utils.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/sagemaker/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/sagemaker/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1485834cb4f561b9ce0a27c48d19b95023174361 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/sagemaker/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/sagemaker/__pycache__/trainer_sm.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/sagemaker/__pycache__/trainer_sm.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..09c4e7704da5ed114864d4c7f6c4b913492bd8a8 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/sagemaker/__pycache__/trainer_sm.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/sagemaker/__pycache__/training_args_sm.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/sagemaker/__pycache__/training_args_sm.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c35769447a6e615b14c3e48cca520c151485fac4 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/sagemaker/__pycache__/training_args_sm.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7cfe94d3085904fe92a622fc981b41c9f5650a4c Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/attention_visualizer.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/attention_visualizer.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..44118ff09bad902fb1b7a2c55fcccf9bd4003705 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/attention_visualizer.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/auto_docstring.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/auto_docstring.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6083db3b826ca8a39a2c8d5cbf9812582cffa00a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/auto_docstring.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/backbone_utils.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/backbone_utils.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aba071898fb721cbc33998686411e1d09536a359 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/backbone_utils.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/bitsandbytes.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/bitsandbytes.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0ab444554387839ce0533fe42cc04f1cd8a9e60f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/bitsandbytes.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/chat_template_utils.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/chat_template_utils.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a8193d0983b98349077e1bb411535f05e41fb1e7 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/chat_template_utils.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/constants.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/constants.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2d1a7562cfa8166d35f8b2ba2e7f267b15a38d88 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/constants.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/deprecation.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/deprecation.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..226a965fe27eb361380181f20c66126ea1723500 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/deprecation.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/doc.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/doc.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7617d2edfa755a18952ef00116838fc14b2f8d45 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/doc.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_detectron2_objects.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_detectron2_objects.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7209c9b7fe6731061cf1695578a11b0352ae9f06 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_detectron2_objects.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_essentia_and_librosa_and_pretty_midi_and_scipy_and_torch_objects.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_essentia_and_librosa_and_pretty_midi_and_scipy_and_torch_objects.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..73f75501a520d589228b73d4aed77d1d23d7be6c Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_essentia_and_librosa_and_pretty_midi_and_scipy_and_torch_objects.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_flax_objects.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_flax_objects.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..db1acfd50391318b49b28eef3c92e7d7e9b65e97 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_flax_objects.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_mistral_common_objects.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_mistral_common_objects.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6f9887dc9caf59c4b09627fa0ac1f637a1bce6cb Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_mistral_common_objects.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_music_objects.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_music_objects.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2b830461193c14ef25a66fb7937903388980d6ce Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_music_objects.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_pt_objects.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_pt_objects.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6044743145de09ae16f17f8ccf674e6e93d9e4a7 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_pt_objects.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_sentencepiece_and_tokenizers_objects.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_sentencepiece_and_tokenizers_objects.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..52065cbc024d5a584350c596283d3efb498a2a4a Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_sentencepiece_and_tokenizers_objects.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_sentencepiece_objects.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_sentencepiece_objects.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fbb88992dc1fc626a45cf679eaf3fcb204c065e3 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_sentencepiece_objects.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_speech_objects.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_speech_objects.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7f14778022271248bae557adb522c1e11796e1ec Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_speech_objects.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_tensorflow_text_objects.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_tensorflow_text_objects.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..422d25fbe818ee8a7b17793b9caff9a0ab703a99 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_tensorflow_text_objects.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_tf_objects.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_tf_objects.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..95f1a308c5c848d4565b2adb839e7e88efb3bd9b Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_tf_objects.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_timm_and_torchvision_objects.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_timm_and_torchvision_objects.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..76d65c92122bd3ac5c2f20ccfdabcad92d04ac6c Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_timm_and_torchvision_objects.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_tokenizers_objects.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_tokenizers_objects.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7fc8c9a3bea09e5382d5b757908b640fcc03c5b5 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_tokenizers_objects.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_torchaudio_objects.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_torchaudio_objects.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b461afebf0ed90831de33d40a0389c76ac392cca Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_torchaudio_objects.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_torchvision_objects.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_torchvision_objects.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7297aada68a43fc34a2476ee67117fba9f5735e2 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_torchvision_objects.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_vision_objects.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_vision_objects.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8863bece0d6a05f077c1e80196d5affc9e8c9276 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/dummy_vision_objects.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/fx.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/fx.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bbf5f7f39923de4bf0a0f95dc58095e211044e2f Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/fx.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/generic.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/generic.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..774bfc737903bac24a535a3f73f12d767f7233dc Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/generic.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/hp_naming.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/hp_naming.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c647542b676bb2b2d3523bead6b3a0c015843f59 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/hp_naming.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/hub.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/hub.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..79ec7158f74a32c8afa5e7e48146ec2fd376a861 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/hub.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/logging.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/logging.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..798ca7e32bf7945828726f6e2f80afbf0230ee76 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/logging.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/metrics.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/metrics.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..df31f376d8b916ac63982ab05fb120058628d6f6 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/metrics.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/model_parallel_utils.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/model_parallel_utils.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..96e2899fa481635151f15dd13e2df4b2671094a6 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/model_parallel_utils.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/notebook.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/notebook.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dac6d94a081e1654dc471f6260defcf31bc19c44 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/notebook.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/peft_utils.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/peft_utils.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..583485889e301c5ebd8cefb8b37f04e77924cbe4 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/peft_utils.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/sentencepiece_model_pb2.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/sentencepiece_model_pb2.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f0c8f8f849222a059a56446360ddc96f9f704c17 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/sentencepiece_model_pb2.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/sentencepiece_model_pb2_new.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/sentencepiece_model_pb2_new.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d43b89367b4cd24d91cf31f128b2243d969a56ee Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/sentencepiece_model_pb2_new.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/versions.cpython-313.pyc b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/versions.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..551f75f27bfe2a19d13b43814819d9f5382c7e79 Binary files /dev/null and b/venv/lib/python3.13/site-packages/transformers/utils/__pycache__/versions.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/typing_extensions-4.15.0.dist-info/licenses/LICENSE b/venv/lib/python3.13/site-packages/typing_extensions-4.15.0.dist-info/licenses/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..f26bcf4d2de6eb136e31006ca3ab447d5e488adf --- /dev/null +++ b/venv/lib/python3.13/site-packages/typing_extensions-4.15.0.dist-info/licenses/LICENSE @@ -0,0 +1,279 @@ +A. HISTORY OF THE SOFTWARE +========================== + +Python was created in the early 1990s by Guido van Rossum at Stichting +Mathematisch Centrum (CWI, see https://www.cwi.nl) in the Netherlands +as a successor of a language called ABC. Guido remains Python's +principal author, although it includes many contributions from others. + +In 1995, Guido continued his work on Python at the Corporation for +National Research Initiatives (CNRI, see https://www.cnri.reston.va.us) +in Reston, Virginia where he released several versions of the +software. + +In May 2000, Guido and the Python core development team moved to +BeOpen.com to form the BeOpen PythonLabs team. In October of the same +year, the PythonLabs team moved to Digital Creations, which became +Zope Corporation. In 2001, the Python Software Foundation (PSF, see +https://www.python.org/psf/) was formed, a non-profit organization +created specifically to own Python-related Intellectual Property. +Zope Corporation was a sponsoring member of the PSF. + +All Python releases are Open Source (see https://opensource.org for +the Open Source Definition). Historically, most, but not all, Python +releases have also been GPL-compatible; the table below summarizes +the various releases. + + Release Derived Year Owner GPL- + from compatible? (1) + + 0.9.0 thru 1.2 1991-1995 CWI yes + 1.3 thru 1.5.2 1.2 1995-1999 CNRI yes + 1.6 1.5.2 2000 CNRI no + 2.0 1.6 2000 BeOpen.com no + 1.6.1 1.6 2001 CNRI yes (2) + 2.1 2.0+1.6.1 2001 PSF no + 2.0.1 2.0+1.6.1 2001 PSF yes + 2.1.1 2.1+2.0.1 2001 PSF yes + 2.1.2 2.1.1 2002 PSF yes + 2.1.3 2.1.2 2002 PSF yes + 2.2 and above 2.1.1 2001-now PSF yes + +Footnotes: + +(1) GPL-compatible doesn't mean that we're distributing Python under + the GPL. All Python licenses, unlike the GPL, let you distribute + a modified version without making your changes open source. The + GPL-compatible licenses make it possible to combine Python with + other software that is released under the GPL; the others don't. + +(2) According to Richard Stallman, 1.6.1 is not GPL-compatible, + because its license has a choice of law clause. According to + CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1 + is "not incompatible" with the GPL. + +Thanks to the many outside volunteers who have worked under Guido's +direction to make these releases possible. + + +B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON +=============================================================== + +Python software and documentation are licensed under the +Python Software Foundation License Version 2. + +Starting with Python 3.8.6, examples, recipes, and other code in +the documentation are dual licensed under the PSF License Version 2 +and the Zero-Clause BSD license. + +Some software incorporated into Python is under different licenses. +The licenses are listed with code falling under that license. + + +PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2 +-------------------------------------------- + +1. This LICENSE AGREEMENT is between the Python Software Foundation +("PSF"), and the Individual or Organization ("Licensee") accessing and +otherwise using this software ("Python") in source or binary form and +its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF hereby +grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce, +analyze, test, perform and/or display publicly, prepare derivative works, +distribute, and otherwise use Python alone or in any derivative version, +provided, however, that PSF's License Agreement and PSF's notice of copyright, +i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, +2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Python Software Foundation; +All Rights Reserved" are retained in Python alone or in any derivative version +prepared by Licensee. + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python. + +4. PSF is making Python available to Licensee on an "AS IS" +basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between PSF and +Licensee. This License Agreement does not grant permission to use PSF +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Python, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0 +------------------------------------------- + +BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1 + +1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an +office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the +Individual or Organization ("Licensee") accessing and otherwise using +this software in source or binary form and its associated +documentation ("the Software"). + +2. Subject to the terms and conditions of this BeOpen Python License +Agreement, BeOpen hereby grants Licensee a non-exclusive, +royalty-free, world-wide license to reproduce, analyze, test, perform +and/or display publicly, prepare derivative works, distribute, and +otherwise use the Software alone or in any derivative version, +provided, however, that the BeOpen Python License is retained in the +Software, alone or in any derivative version prepared by Licensee. + +3. BeOpen is making the Software available to Licensee on an "AS IS" +basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE +SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS +AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY +DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +5. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +6. This License Agreement shall be governed by and interpreted in all +respects by the law of the State of California, excluding conflict of +law provisions. Nothing in this License Agreement shall be deemed to +create any relationship of agency, partnership, or joint venture +between BeOpen and Licensee. This License Agreement does not grant +permission to use BeOpen trademarks or trade names in a trademark +sense to endorse or promote products or services of Licensee, or any +third party. As an exception, the "BeOpen Python" logos available at +http://www.pythonlabs.com/logos.html may be used according to the +permissions granted on that web page. + +7. By copying, installing or otherwise using the software, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + + +CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1 +--------------------------------------- + +1. This LICENSE AGREEMENT is between the Corporation for National +Research Initiatives, having an office at 1895 Preston White Drive, +Reston, VA 20191 ("CNRI"), and the Individual or Organization +("Licensee") accessing and otherwise using Python 1.6.1 software in +source or binary form and its associated documentation. + +2. Subject to the terms and conditions of this License Agreement, CNRI +hereby grants Licensee a nonexclusive, royalty-free, world-wide +license to reproduce, analyze, test, perform and/or display publicly, +prepare derivative works, distribute, and otherwise use Python 1.6.1 +alone or in any derivative version, provided, however, that CNRI's +License Agreement and CNRI's notice of copyright, i.e., "Copyright (c) +1995-2001 Corporation for National Research Initiatives; All Rights +Reserved" are retained in Python 1.6.1 alone or in any derivative +version prepared by Licensee. Alternately, in lieu of CNRI's License +Agreement, Licensee may substitute the following text (omitting the +quotes): "Python 1.6.1 is made available subject to the terms and +conditions in CNRI's License Agreement. This Agreement together with +Python 1.6.1 may be located on the internet using the following +unique, persistent identifier (known as a handle): 1895.22/1013. This +Agreement may also be obtained from a proxy server on the internet +using the following URL: http://hdl.handle.net/1895.22/1013". + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python 1.6.1 or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python 1.6.1. + +4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS" +basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR +IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND +DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS +FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT +INFRINGE ANY THIRD PARTY RIGHTS. + +5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. This License Agreement shall be governed by the federal +intellectual property law of the United States, including without +limitation the federal copyright law, and, to the extent such +U.S. federal law does not apply, by the law of the Commonwealth of +Virginia, excluding Virginia's conflict of law provisions. +Notwithstanding the foregoing, with regard to derivative works based +on Python 1.6.1 that incorporate non-separable material that was +previously distributed under the GNU General Public License (GPL), the +law of the Commonwealth of Virginia shall govern this License +Agreement only as to issues arising under or with respect to +Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this +License Agreement shall be deemed to create any relationship of +agency, partnership, or joint venture between CNRI and Licensee. This +License Agreement does not grant permission to use CNRI trademarks or +trade name in a trademark sense to endorse or promote products or +services of Licensee, or any third party. + +8. By clicking on the "ACCEPT" button where indicated, or by copying, +installing or otherwise using Python 1.6.1, Licensee agrees to be +bound by the terms and conditions of this License Agreement. + + ACCEPT + + +CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2 +-------------------------------------------------- + +Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam, +The Netherlands. All rights reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Stichting Mathematisch +Centrum or CWI not be used in advertising or publicity pertaining to +distribution of the software without specific, written prior +permission. + +STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE +FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +ZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION +---------------------------------------------------------------------- + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/venv/lib/python3.13/site-packages/urllib3/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..455f3d354e275cf025f527ed78c3acf2a2126b45 Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/__pycache__/_base_connection.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/__pycache__/_base_connection.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d6f9f6bcf7412f9fc2c5f95e364ff325cebcdc20 Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/__pycache__/_base_connection.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/__pycache__/_collections.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/__pycache__/_collections.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4cbb623289341b38db580c40269fb6d1365e1db0 Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/__pycache__/_collections.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/__pycache__/_request_methods.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/__pycache__/_request_methods.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3521354513873f11407181eebbc483e8589723d4 Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/__pycache__/_request_methods.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/__pycache__/_version.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/__pycache__/_version.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ef132aed5b21703eac311358c872f2802cd48809 Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/__pycache__/_version.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/__pycache__/connection.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/__pycache__/connection.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9ab944da784231d3b7966f6a849101185d7d13c9 Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/__pycache__/connection.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/__pycache__/connectionpool.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/__pycache__/connectionpool.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..92fc2aa0be9ff69e5df6f3f473524d76b80a50e6 Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/__pycache__/connectionpool.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/__pycache__/exceptions.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/__pycache__/exceptions.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..30e0b5ab418ab57f68d6f2184ae970554d4af131 Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/__pycache__/exceptions.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/__pycache__/fields.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/__pycache__/fields.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..35015c01b3d14261fc520b65e22dd21954c1ca07 Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/__pycache__/fields.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/__pycache__/filepost.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/__pycache__/filepost.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a6e41d7b302c6b5e6572474d666c0b7704034432 Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/__pycache__/filepost.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/__pycache__/poolmanager.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/__pycache__/poolmanager.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8070234eb235fea6a85a777cecad85b83d4a1724 Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/__pycache__/poolmanager.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/__pycache__/response.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/__pycache__/response.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..44967542c8460d6933d66ea4fd148c0663e2278f Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/__pycache__/response.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/contrib/__init__.py b/venv/lib/python3.13/site-packages/urllib3/contrib/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/venv/lib/python3.13/site-packages/urllib3/contrib/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/contrib/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..babe10a5193dab443903a1d0f76e816396c509bc Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/contrib/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/contrib/__pycache__/pyopenssl.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/contrib/__pycache__/pyopenssl.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c195a0a19769101e9ce835d3b86a2c72b4a3515e Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/contrib/__pycache__/pyopenssl.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/contrib/__pycache__/socks.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/contrib/__pycache__/socks.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..46b8350af4de6e9082bba95a00f9a9acdfd334fa Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/contrib/__pycache__/socks.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/__init__.py b/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..8a3c5bebdc151eef715663628a697118bb2932ed --- /dev/null +++ b/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/__init__.py @@ -0,0 +1,16 @@ +from __future__ import annotations + +import urllib3.connection + +from ...connectionpool import HTTPConnectionPool, HTTPSConnectionPool +from .connection import EmscriptenHTTPConnection, EmscriptenHTTPSConnection + + +def inject_into_urllib3() -> None: + # override connection classes to use emscripten specific classes + # n.b. mypy complains about the overriding of classes below + # if it isn't ignored + HTTPConnectionPool.ConnectionCls = EmscriptenHTTPConnection + HTTPSConnectionPool.ConnectionCls = EmscriptenHTTPSConnection + urllib3.connection.HTTPConnection = EmscriptenHTTPConnection # type: ignore[misc,assignment] + urllib3.connection.HTTPSConnection = EmscriptenHTTPSConnection # type: ignore[misc,assignment] diff --git a/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d32fe0e5e511e6e8a874683e7a85bbc05759ba08 Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/__pycache__/connection.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/__pycache__/connection.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aaf031539f49cc21bc18fea4b5b76c11a3b0892a Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/__pycache__/connection.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/__pycache__/fetch.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/__pycache__/fetch.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..428fe8e1b59a239f7943a0991b742259051b04c3 Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/__pycache__/fetch.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/__pycache__/request.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/__pycache__/request.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..78b223629191eeeed99f8c2584ba3d219d977885 Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/__pycache__/request.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/__pycache__/response.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/__pycache__/response.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e29ed9ac519cd3d520ed9da94b8ae91be42c7572 Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/__pycache__/response.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/connection.py b/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/connection.py new file mode 100644 index 0000000000000000000000000000000000000000..41bfd2797ffeef54fa50f708026c4cc44b7d2c12 --- /dev/null +++ b/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/connection.py @@ -0,0 +1,255 @@ +from __future__ import annotations + +import os +import typing + +# use http.client.HTTPException for consistency with non-emscripten +from http.client import HTTPException as HTTPException # noqa: F401 +from http.client import ResponseNotReady + +from ..._base_connection import _TYPE_BODY +from ...connection import HTTPConnection, ProxyConfig, port_by_scheme +from ...exceptions import TimeoutError +from ...response import BaseHTTPResponse +from ...util.connection import _TYPE_SOCKET_OPTIONS +from ...util.timeout import _DEFAULT_TIMEOUT, _TYPE_TIMEOUT +from ...util.url import Url +from .fetch import _RequestError, _TimeoutError, send_request, send_streaming_request +from .request import EmscriptenRequest +from .response import EmscriptenHttpResponseWrapper, EmscriptenResponse + +if typing.TYPE_CHECKING: + from ..._base_connection import BaseHTTPConnection, BaseHTTPSConnection + + +class EmscriptenHTTPConnection: + default_port: typing.ClassVar[int] = port_by_scheme["http"] + default_socket_options: typing.ClassVar[_TYPE_SOCKET_OPTIONS] + + timeout: None | (float) + + host: str + port: int + blocksize: int + source_address: tuple[str, int] | None + socket_options: _TYPE_SOCKET_OPTIONS | None + + proxy: Url | None + proxy_config: ProxyConfig | None + + is_verified: bool = False + proxy_is_verified: bool | None = None + + _response: EmscriptenResponse | None + + def __init__( + self, + host: str, + port: int = 0, + *, + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + source_address: tuple[str, int] | None = None, + blocksize: int = 8192, + socket_options: _TYPE_SOCKET_OPTIONS | None = None, + proxy: Url | None = None, + proxy_config: ProxyConfig | None = None, + ) -> None: + self.host = host + self.port = port + self.timeout = timeout if isinstance(timeout, float) else 0.0 + self.scheme = "http" + self._closed = True + self._response = None + # ignore these things because we don't + # have control over that stuff + self.proxy = None + self.proxy_config = None + self.blocksize = blocksize + self.source_address = None + self.socket_options = None + self.is_verified = False + + def set_tunnel( + self, + host: str, + port: int | None = 0, + headers: typing.Mapping[str, str] | None = None, + scheme: str = "http", + ) -> None: + pass + + def connect(self) -> None: + pass + + def request( + self, + method: str, + url: str, + body: _TYPE_BODY | None = None, + headers: typing.Mapping[str, str] | None = None, + # We know *at least* botocore is depending on the order of the + # first 3 parameters so to be safe we only mark the later ones + # as keyword-only to ensure we have space to extend. + *, + chunked: bool = False, + preload_content: bool = True, + decode_content: bool = True, + enforce_content_length: bool = True, + ) -> None: + self._closed = False + if url.startswith("/"): + # no scheme / host / port included, make a full url + url = f"{self.scheme}://{self.host}:{self.port}" + url + request = EmscriptenRequest( + url=url, + method=method, + timeout=self.timeout if self.timeout else 0, + decode_content=decode_content, + ) + request.set_body(body) + if headers: + for k, v in headers.items(): + request.set_header(k, v) + self._response = None + try: + if not preload_content: + self._response = send_streaming_request(request) + if self._response is None: + self._response = send_request(request) + except _TimeoutError as e: + raise TimeoutError(e.message) from e + except _RequestError as e: + raise HTTPException(e.message) from e + + def getresponse(self) -> BaseHTTPResponse: + if self._response is not None: + return EmscriptenHttpResponseWrapper( + internal_response=self._response, + url=self._response.request.url, + connection=self, + ) + else: + raise ResponseNotReady() + + def close(self) -> None: + self._closed = True + self._response = None + + @property + def is_closed(self) -> bool: + """Whether the connection either is brand new or has been previously closed. + If this property is True then both ``is_connected`` and ``has_connected_to_proxy`` + properties must be False. + """ + return self._closed + + @property + def is_connected(self) -> bool: + """Whether the connection is actively connected to any origin (proxy or target)""" + return True + + @property + def has_connected_to_proxy(self) -> bool: + """Whether the connection has successfully connected to its proxy. + This returns False if no proxy is in use. Used to determine whether + errors are coming from the proxy layer or from tunnelling to the target origin. + """ + return False + + +class EmscriptenHTTPSConnection(EmscriptenHTTPConnection): + default_port = port_by_scheme["https"] + # all this is basically ignored, as browser handles https + cert_reqs: int | str | None = None + ca_certs: str | None = None + ca_cert_dir: str | None = None + ca_cert_data: None | str | bytes = None + cert_file: str | None + key_file: str | None + key_password: str | None + ssl_context: typing.Any | None + ssl_version: int | str | None = None + ssl_minimum_version: int | None = None + ssl_maximum_version: int | None = None + assert_hostname: None | str | typing.Literal[False] + assert_fingerprint: str | None = None + + def __init__( + self, + host: str, + port: int = 0, + *, + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + source_address: tuple[str, int] | None = None, + blocksize: int = 16384, + socket_options: ( + None | _TYPE_SOCKET_OPTIONS + ) = HTTPConnection.default_socket_options, + proxy: Url | None = None, + proxy_config: ProxyConfig | None = None, + cert_reqs: int | str | None = None, + assert_hostname: None | str | typing.Literal[False] = None, + assert_fingerprint: str | None = None, + server_hostname: str | None = None, + ssl_context: typing.Any | None = None, + ca_certs: str | None = None, + ca_cert_dir: str | None = None, + ca_cert_data: None | str | bytes = None, + ssl_minimum_version: int | None = None, + ssl_maximum_version: int | None = None, + ssl_version: int | str | None = None, # Deprecated + cert_file: str | None = None, + key_file: str | None = None, + key_password: str | None = None, + ) -> None: + super().__init__( + host, + port=port, + timeout=timeout, + source_address=source_address, + blocksize=blocksize, + socket_options=socket_options, + proxy=proxy, + proxy_config=proxy_config, + ) + self.scheme = "https" + + self.key_file = key_file + self.cert_file = cert_file + self.key_password = key_password + self.ssl_context = ssl_context + self.server_hostname = server_hostname + self.assert_hostname = assert_hostname + self.assert_fingerprint = assert_fingerprint + self.ssl_version = ssl_version + self.ssl_minimum_version = ssl_minimum_version + self.ssl_maximum_version = ssl_maximum_version + self.ca_certs = ca_certs and os.path.expanduser(ca_certs) + self.ca_cert_dir = ca_cert_dir and os.path.expanduser(ca_cert_dir) + self.ca_cert_data = ca_cert_data + + self.cert_reqs = None + + # The browser will automatically verify all requests. + # We have no control over that setting. + self.is_verified = True + + def set_cert( + self, + key_file: str | None = None, + cert_file: str | None = None, + cert_reqs: int | str | None = None, + key_password: str | None = None, + ca_certs: str | None = None, + assert_hostname: None | str | typing.Literal[False] = None, + assert_fingerprint: str | None = None, + ca_cert_dir: str | None = None, + ca_cert_data: None | str | bytes = None, + ) -> None: + pass + + +# verify that this class implements BaseHTTP(s) connection correctly +if typing.TYPE_CHECKING: + _supports_http_protocol: BaseHTTPConnection = EmscriptenHTTPConnection("", 0) + _supports_https_protocol: BaseHTTPSConnection = EmscriptenHTTPSConnection("", 0) diff --git a/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/emscripten_fetch_worker.js b/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/emscripten_fetch_worker.js new file mode 100644 index 0000000000000000000000000000000000000000..243b86222f90a9be4b6b4ce0bf997eefd29289af --- /dev/null +++ b/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/emscripten_fetch_worker.js @@ -0,0 +1,110 @@ +let Status = { + SUCCESS_HEADER: -1, + SUCCESS_EOF: -2, + ERROR_TIMEOUT: -3, + ERROR_EXCEPTION: -4, +}; + +let connections = {}; +let nextConnectionID = 1; +const encoder = new TextEncoder(); + +self.addEventListener("message", async function (event) { + if (event.data.close) { + let connectionID = event.data.close; + delete connections[connectionID]; + return; + } else if (event.data.getMore) { + let connectionID = event.data.getMore; + let { curOffset, value, reader, intBuffer, byteBuffer } = + connections[connectionID]; + // if we still have some in buffer, then just send it back straight away + if (!value || curOffset >= value.length) { + // read another buffer if required + try { + let readResponse = await reader.read(); + + if (readResponse.done) { + // read everything - clear connection and return + delete connections[connectionID]; + Atomics.store(intBuffer, 0, Status.SUCCESS_EOF); + Atomics.notify(intBuffer, 0); + // finished reading successfully + // return from event handler + return; + } + curOffset = 0; + connections[connectionID].value = readResponse.value; + value = readResponse.value; + } catch (error) { + console.log("Request exception:", error); + let errorBytes = encoder.encode(error.message); + let written = errorBytes.length; + byteBuffer.set(errorBytes); + intBuffer[1] = written; + Atomics.store(intBuffer, 0, Status.ERROR_EXCEPTION); + Atomics.notify(intBuffer, 0); + } + } + + // send as much buffer as we can + let curLen = value.length - curOffset; + if (curLen > byteBuffer.length) { + curLen = byteBuffer.length; + } + byteBuffer.set(value.subarray(curOffset, curOffset + curLen), 0); + + Atomics.store(intBuffer, 0, curLen); // store current length in bytes + Atomics.notify(intBuffer, 0); + curOffset += curLen; + connections[connectionID].curOffset = curOffset; + + return; + } else { + // start fetch + let connectionID = nextConnectionID; + nextConnectionID += 1; + const intBuffer = new Int32Array(event.data.buffer); + const byteBuffer = new Uint8Array(event.data.buffer, 8); + try { + const response = await fetch(event.data.url, event.data.fetchParams); + // return the headers first via textencoder + var headers = []; + for (const pair of response.headers.entries()) { + headers.push([pair[0], pair[1]]); + } + let headerObj = { + headers: headers, + status: response.status, + connectionID, + }; + const headerText = JSON.stringify(headerObj); + let headerBytes = encoder.encode(headerText); + let written = headerBytes.length; + byteBuffer.set(headerBytes); + intBuffer[1] = written; + // make a connection + connections[connectionID] = { + reader: response.body.getReader(), + intBuffer: intBuffer, + byteBuffer: byteBuffer, + value: undefined, + curOffset: 0, + }; + // set header ready + Atomics.store(intBuffer, 0, Status.SUCCESS_HEADER); + Atomics.notify(intBuffer, 0); + // all fetching after this goes through a new postmessage call with getMore + // this allows for parallel requests + } catch (error) { + console.log("Request exception:", error); + let errorBytes = encoder.encode(error.message); + let written = errorBytes.length; + byteBuffer.set(errorBytes); + intBuffer[1] = written; + Atomics.store(intBuffer, 0, Status.ERROR_EXCEPTION); + Atomics.notify(intBuffer, 0); + } + } +}); +self.postMessage({ inited: true }); diff --git a/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/fetch.py b/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/fetch.py new file mode 100644 index 0000000000000000000000000000000000000000..66958217a9fdc8a03eb991d6da2558d7e7910150 --- /dev/null +++ b/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/fetch.py @@ -0,0 +1,728 @@ +""" +Support for streaming http requests in emscripten. + +A few caveats - + +If your browser (or Node.js) has WebAssembly JavaScript Promise Integration enabled +https://github.com/WebAssembly/js-promise-integration/blob/main/proposals/js-promise-integration/Overview.md +*and* you launch pyodide using `pyodide.runPythonAsync`, this will fetch data using the +JavaScript asynchronous fetch api (wrapped via `pyodide.ffi.call_sync`). In this case +timeouts and streaming should just work. + +Otherwise, it uses a combination of XMLHttpRequest and a web-worker for streaming. + +This approach has several caveats: + +Firstly, you can't do streaming http in the main UI thread, because atomics.wait isn't allowed. +Streaming only works if you're running pyodide in a web worker. + +Secondly, this uses an extra web worker and SharedArrayBuffer to do the asynchronous fetch +operation, so it requires that you have crossOriginIsolation enabled, by serving over https +(or from localhost) with the two headers below set: + + Cross-Origin-Opener-Policy: same-origin + Cross-Origin-Embedder-Policy: require-corp + +You can tell if cross origin isolation is successfully enabled by looking at the global crossOriginIsolated variable in +JavaScript console. If it isn't, streaming requests will fallback to XMLHttpRequest, i.e. getting the whole +request into a buffer and then returning it. it shows a warning in the JavaScript console in this case. + +Finally, the webworker which does the streaming fetch is created on initial import, but will only be started once +control is returned to javascript. Call `await wait_for_streaming_ready()` to wait for streaming fetch. + +NB: in this code, there are a lot of JavaScript objects. They are named js_* +to make it clear what type of object they are. +""" + +from __future__ import annotations + +import io +import json +from email.parser import Parser +from importlib.resources import files +from typing import TYPE_CHECKING, Any + +import js # type: ignore[import-not-found] +from pyodide.ffi import ( # type: ignore[import-not-found] + JsArray, + JsException, + JsProxy, + to_js, +) + +if TYPE_CHECKING: + from typing_extensions import Buffer + +from .request import EmscriptenRequest +from .response import EmscriptenResponse + +""" +There are some headers that trigger unintended CORS preflight requests. +See also https://github.com/koenvo/pyodide-http/issues/22 +""" +HEADERS_TO_IGNORE = ("user-agent",) + +SUCCESS_HEADER = -1 +SUCCESS_EOF = -2 +ERROR_TIMEOUT = -3 +ERROR_EXCEPTION = -4 + +_STREAMING_WORKER_CODE = ( + files(__package__) + .joinpath("emscripten_fetch_worker.js") + .read_text(encoding="utf-8") +) + + +class _RequestError(Exception): + def __init__( + self, + message: str | None = None, + *, + request: EmscriptenRequest | None = None, + response: EmscriptenResponse | None = None, + ): + self.request = request + self.response = response + self.message = message + super().__init__(self.message) + + +class _StreamingError(_RequestError): + pass + + +class _TimeoutError(_RequestError): + pass + + +def _obj_from_dict(dict_val: dict[str, Any]) -> JsProxy: + return to_js(dict_val, dict_converter=js.Object.fromEntries) + + +class _ReadStream(io.RawIOBase): + def __init__( + self, + int_buffer: JsArray, + byte_buffer: JsArray, + timeout: float, + worker: JsProxy, + connection_id: int, + request: EmscriptenRequest, + ): + self.int_buffer = int_buffer + self.byte_buffer = byte_buffer + self.read_pos = 0 + self.read_len = 0 + self.connection_id = connection_id + self.worker = worker + self.timeout = int(1000 * timeout) if timeout > 0 else None + self.is_live = True + self._is_closed = False + self.request: EmscriptenRequest | None = request + + def __del__(self) -> None: + self.close() + + # this is compatible with _base_connection + def is_closed(self) -> bool: + return self._is_closed + + # for compatibility with RawIOBase + @property + def closed(self) -> bool: + return self.is_closed() + + def close(self) -> None: + if self.is_closed(): + return + self.read_len = 0 + self.read_pos = 0 + self.int_buffer = None + self.byte_buffer = None + self._is_closed = True + self.request = None + if self.is_live: + self.worker.postMessage(_obj_from_dict({"close": self.connection_id})) + self.is_live = False + super().close() + + def readable(self) -> bool: + return True + + def writable(self) -> bool: + return False + + def seekable(self) -> bool: + return False + + def readinto(self, byte_obj: Buffer) -> int: + if not self.int_buffer: + raise _StreamingError( + "No buffer for stream in _ReadStream.readinto", + request=self.request, + response=None, + ) + if self.read_len == 0: + # wait for the worker to send something + js.Atomics.store(self.int_buffer, 0, ERROR_TIMEOUT) + self.worker.postMessage(_obj_from_dict({"getMore": self.connection_id})) + if ( + js.Atomics.wait(self.int_buffer, 0, ERROR_TIMEOUT, self.timeout) + == "timed-out" + ): + raise _TimeoutError + data_len = self.int_buffer[0] + if data_len > 0: + self.read_len = data_len + self.read_pos = 0 + elif data_len == ERROR_EXCEPTION: + string_len = self.int_buffer[1] + # decode the error string + js_decoder = js.TextDecoder.new() + json_str = js_decoder.decode(self.byte_buffer.slice(0, string_len)) + raise _StreamingError( + f"Exception thrown in fetch: {json_str}", + request=self.request, + response=None, + ) + else: + # EOF, free the buffers and return zero + # and free the request + self.is_live = False + self.close() + return 0 + # copy from int32array to python bytes + ret_length = min(self.read_len, len(memoryview(byte_obj))) + subarray = self.byte_buffer.subarray( + self.read_pos, self.read_pos + ret_length + ).to_py() + memoryview(byte_obj)[0:ret_length] = subarray + self.read_len -= ret_length + self.read_pos += ret_length + return ret_length + + +class _StreamingFetcher: + def __init__(self) -> None: + # make web-worker and data buffer on startup + self.streaming_ready = False + + js_data_blob = js.Blob.new( + to_js([_STREAMING_WORKER_CODE], create_pyproxies=False), + _obj_from_dict({"type": "application/javascript"}), + ) + + def promise_resolver(js_resolve_fn: JsProxy, js_reject_fn: JsProxy) -> None: + def onMsg(e: JsProxy) -> None: + self.streaming_ready = True + js_resolve_fn(e) + + def onErr(e: JsProxy) -> None: + js_reject_fn(e) # Defensive: never happens in ci + + self.js_worker.onmessage = onMsg + self.js_worker.onerror = onErr + + js_data_url = js.URL.createObjectURL(js_data_blob) + self.js_worker = js.globalThis.Worker.new(js_data_url) + self.js_worker_ready_promise = js.globalThis.Promise.new(promise_resolver) + + def send(self, request: EmscriptenRequest) -> EmscriptenResponse: + headers = { + k: v for k, v in request.headers.items() if k not in HEADERS_TO_IGNORE + } + + body = request.body + fetch_data = {"headers": headers, "body": to_js(body), "method": request.method} + # start the request off in the worker + timeout = int(1000 * request.timeout) if request.timeout > 0 else None + js_shared_buffer = js.SharedArrayBuffer.new(1048576) + js_int_buffer = js.Int32Array.new(js_shared_buffer) + js_byte_buffer = js.Uint8Array.new(js_shared_buffer, 8) + + js.Atomics.store(js_int_buffer, 0, ERROR_TIMEOUT) + js.Atomics.notify(js_int_buffer, 0) + js_absolute_url = js.URL.new(request.url, js.location).href + self.js_worker.postMessage( + _obj_from_dict( + { + "buffer": js_shared_buffer, + "url": js_absolute_url, + "fetchParams": fetch_data, + } + ) + ) + # wait for the worker to send something + js.Atomics.wait(js_int_buffer, 0, ERROR_TIMEOUT, timeout) + if js_int_buffer[0] == ERROR_TIMEOUT: + raise _TimeoutError( + "Timeout connecting to streaming request", + request=request, + response=None, + ) + elif js_int_buffer[0] == SUCCESS_HEADER: + # got response + # header length is in second int of intBuffer + string_len = js_int_buffer[1] + # decode the rest to a JSON string + js_decoder = js.TextDecoder.new() + # this does a copy (the slice) because decode can't work on shared array + # for some silly reason + json_str = js_decoder.decode(js_byte_buffer.slice(0, string_len)) + # get it as an object + response_obj = json.loads(json_str) + return EmscriptenResponse( + request=request, + status_code=response_obj["status"], + headers=response_obj["headers"], + body=_ReadStream( + js_int_buffer, + js_byte_buffer, + request.timeout, + self.js_worker, + response_obj["connectionID"], + request, + ), + ) + elif js_int_buffer[0] == ERROR_EXCEPTION: + string_len = js_int_buffer[1] + # decode the error string + js_decoder = js.TextDecoder.new() + json_str = js_decoder.decode(js_byte_buffer.slice(0, string_len)) + raise _StreamingError( + f"Exception thrown in fetch: {json_str}", request=request, response=None + ) + else: + raise _StreamingError( + f"Unknown status from worker in fetch: {js_int_buffer[0]}", + request=request, + response=None, + ) + + +class _JSPIReadStream(io.RawIOBase): + """ + A read stream that uses pyodide.ffi.run_sync to read from a JavaScript fetch + response. This requires support for WebAssembly JavaScript Promise Integration + in the containing browser, and for pyodide to be launched via runPythonAsync. + + :param js_read_stream: + The JavaScript stream reader + + :param timeout: + Timeout in seconds + + :param request: + The request we're handling + + :param response: + The response this stream relates to + + :param js_abort_controller: + A JavaScript AbortController object, used for timeouts + """ + + def __init__( + self, + js_read_stream: Any, + timeout: float, + request: EmscriptenRequest, + response: EmscriptenResponse, + js_abort_controller: Any, # JavaScript AbortController for timeouts + ): + self.js_read_stream = js_read_stream + self.timeout = timeout + self._is_closed = False + self._is_done = False + self.request: EmscriptenRequest | None = request + self.response: EmscriptenResponse | None = response + self.current_buffer = None + self.current_buffer_pos = 0 + self.js_abort_controller = js_abort_controller + + def __del__(self) -> None: + self.close() + + # this is compatible with _base_connection + def is_closed(self) -> bool: + return self._is_closed + + # for compatibility with RawIOBase + @property + def closed(self) -> bool: + return self.is_closed() + + def close(self) -> None: + if self.is_closed(): + return + self.read_len = 0 + self.read_pos = 0 + self.js_read_stream.cancel() + self.js_read_stream = None + self._is_closed = True + self._is_done = True + self.request = None + self.response = None + super().close() + + def readable(self) -> bool: + return True + + def writable(self) -> bool: + return False + + def seekable(self) -> bool: + return False + + def _get_next_buffer(self) -> bool: + result_js = _run_sync_with_timeout( + self.js_read_stream.read(), + self.timeout, + self.js_abort_controller, + request=self.request, + response=self.response, + ) + if result_js.done: + self._is_done = True + return False + else: + self.current_buffer = result_js.value.to_py() + self.current_buffer_pos = 0 + return True + + def readinto(self, byte_obj: Buffer) -> int: + if self.current_buffer is None: + if not self._get_next_buffer() or self.current_buffer is None: + self.close() + return 0 + ret_length = min( + len(byte_obj), len(self.current_buffer) - self.current_buffer_pos + ) + byte_obj[0:ret_length] = self.current_buffer[ + self.current_buffer_pos : self.current_buffer_pos + ret_length + ] + self.current_buffer_pos += ret_length + if self.current_buffer_pos == len(self.current_buffer): + self.current_buffer = None + return ret_length + + +# check if we are in a worker or not +def is_in_browser_main_thread() -> bool: + return hasattr(js, "window") and hasattr(js, "self") and js.self == js.window + + +def is_cross_origin_isolated() -> bool: + return hasattr(js, "crossOriginIsolated") and js.crossOriginIsolated + + +def is_in_node() -> bool: + return ( + hasattr(js, "process") + and hasattr(js.process, "release") + and hasattr(js.process.release, "name") + and js.process.release.name == "node" + ) + + +def is_worker_available() -> bool: + return hasattr(js, "Worker") and hasattr(js, "Blob") + + +_fetcher: _StreamingFetcher | None = None + +if is_worker_available() and ( + (is_cross_origin_isolated() and not is_in_browser_main_thread()) + and (not is_in_node()) +): + _fetcher = _StreamingFetcher() +else: + _fetcher = None + + +NODE_JSPI_ERROR = ( + "urllib3 only works in Node.js with pyodide.runPythonAsync" + " and requires the flag --experimental-wasm-stack-switching in " + " versions of node <24." +) + + +def send_streaming_request(request: EmscriptenRequest) -> EmscriptenResponse | None: + if has_jspi(): + return send_jspi_request(request, True) + elif is_in_node(): + raise _RequestError( + message=NODE_JSPI_ERROR, + request=request, + response=None, + ) + + if _fetcher and streaming_ready(): + return _fetcher.send(request) + else: + _show_streaming_warning() + return None + + +_SHOWN_TIMEOUT_WARNING = False + + +def _show_timeout_warning() -> None: + global _SHOWN_TIMEOUT_WARNING + if not _SHOWN_TIMEOUT_WARNING: + _SHOWN_TIMEOUT_WARNING = True + message = "Warning: Timeout is not available on main browser thread" + js.console.warn(message) + + +_SHOWN_STREAMING_WARNING = False + + +def _show_streaming_warning() -> None: + global _SHOWN_STREAMING_WARNING + if not _SHOWN_STREAMING_WARNING: + _SHOWN_STREAMING_WARNING = True + message = "Can't stream HTTP requests because: \n" + if not is_cross_origin_isolated(): + message += " Page is not cross-origin isolated\n" + if is_in_browser_main_thread(): + message += " Python is running in main browser thread\n" + if not is_worker_available(): + message += " Worker or Blob classes are not available in this environment." # Defensive: this is always False in browsers that we test in + if streaming_ready() is False: + message += """ Streaming fetch worker isn't ready. If you want to be sure that streaming fetch +is working, you need to call: 'await urllib3.contrib.emscripten.fetch.wait_for_streaming_ready()`""" + from js import console + + console.warn(message) + + +def send_request(request: EmscriptenRequest) -> EmscriptenResponse: + if has_jspi(): + return send_jspi_request(request, False) + elif is_in_node(): + raise _RequestError( + message=NODE_JSPI_ERROR, + request=request, + response=None, + ) + try: + js_xhr = js.XMLHttpRequest.new() + + if not is_in_browser_main_thread(): + js_xhr.responseType = "arraybuffer" + if request.timeout: + js_xhr.timeout = int(request.timeout * 1000) + else: + js_xhr.overrideMimeType("text/plain; charset=ISO-8859-15") + if request.timeout: + # timeout isn't available on the main thread - show a warning in console + # if it is set + _show_timeout_warning() + + js_xhr.open(request.method, request.url, False) + for name, value in request.headers.items(): + if name.lower() not in HEADERS_TO_IGNORE: + js_xhr.setRequestHeader(name, value) + + js_xhr.send(to_js(request.body)) + + headers = dict(Parser().parsestr(js_xhr.getAllResponseHeaders())) + + if not is_in_browser_main_thread(): + body = js_xhr.response.to_py().tobytes() + else: + body = js_xhr.response.encode("ISO-8859-15") + return EmscriptenResponse( + status_code=js_xhr.status, headers=headers, body=body, request=request + ) + except JsException as err: + if err.name == "TimeoutError": + raise _TimeoutError(err.message, request=request) + elif err.name == "NetworkError": + raise _RequestError(err.message, request=request) + else: + # general http error + raise _RequestError(err.message, request=request) + + +def send_jspi_request( + request: EmscriptenRequest, streaming: bool +) -> EmscriptenResponse: + """ + Send a request using WebAssembly JavaScript Promise Integration + to wrap the asynchronous JavaScript fetch api (experimental). + + :param request: + Request to send + + :param streaming: + Whether to stream the response + + :return: The response object + :rtype: EmscriptenResponse + """ + timeout = request.timeout + js_abort_controller = js.AbortController.new() + headers = {k: v for k, v in request.headers.items() if k not in HEADERS_TO_IGNORE} + req_body = request.body + fetch_data = { + "headers": headers, + "body": to_js(req_body), + "method": request.method, + "signal": js_abort_controller.signal, + } + # Node.js returns the whole response (unlike opaqueredirect in browsers), + # so urllib3 can set `redirect: manual` to control redirects itself. + # https://stackoverflow.com/a/78524615 + if _is_node_js(): + fetch_data["redirect"] = "manual" + # Call JavaScript fetch (async api, returns a promise) + fetcher_promise_js = js.fetch(request.url, _obj_from_dict(fetch_data)) + # Now suspend WebAssembly until we resolve that promise + # or time out. + response_js = _run_sync_with_timeout( + fetcher_promise_js, + timeout, + js_abort_controller, + request=request, + response=None, + ) + headers = {} + header_iter = response_js.headers.entries() + while True: + iter_value_js = header_iter.next() + if getattr(iter_value_js, "done", False): + break + else: + headers[str(iter_value_js.value[0])] = str(iter_value_js.value[1]) + status_code = response_js.status + body: bytes | io.RawIOBase = b"" + + response = EmscriptenResponse( + status_code=status_code, headers=headers, body=b"", request=request + ) + if streaming: + # get via inputstream + if response_js.body is not None: + # get a reader from the fetch response + body_stream_js = response_js.body.getReader() + body = _JSPIReadStream( + body_stream_js, timeout, request, response, js_abort_controller + ) + else: + # get directly via arraybuffer + # n.b. this is another async JavaScript call. + body = _run_sync_with_timeout( + response_js.arrayBuffer(), + timeout, + js_abort_controller, + request=request, + response=response, + ).to_py() + response.body = body + return response + + +def _run_sync_with_timeout( + promise: Any, + timeout: float, + js_abort_controller: Any, + request: EmscriptenRequest | None, + response: EmscriptenResponse | None, +) -> Any: + """ + Await a JavaScript promise synchronously with a timeout which is implemented + via the AbortController + + :param promise: + Javascript promise to await + + :param timeout: + Timeout in seconds + + :param js_abort_controller: + A JavaScript AbortController object, used on timeout + + :param request: + The request being handled + + :param response: + The response being handled (if it exists yet) + + :raises _TimeoutError: If the request times out + :raises _RequestError: If the request raises a JavaScript exception + + :return: The result of awaiting the promise. + """ + timer_id = None + if timeout > 0: + timer_id = js.setTimeout( + js_abort_controller.abort.bind(js_abort_controller), int(timeout * 1000) + ) + try: + from pyodide.ffi import run_sync + + # run_sync here uses WebAssembly JavaScript Promise Integration to + # suspend python until the JavaScript promise resolves. + return run_sync(promise) + except JsException as err: + if err.name == "AbortError": + raise _TimeoutError( + message="Request timed out", request=request, response=response + ) + else: + raise _RequestError(message=err.message, request=request, response=response) + finally: + if timer_id is not None: + js.clearTimeout(timer_id) + + +def has_jspi() -> bool: + """ + Return true if jspi can be used. + + This requires both browser support and also WebAssembly + to be in the correct state - i.e. that the javascript + call into python was async not sync. + + :return: True if jspi can be used. + :rtype: bool + """ + try: + from pyodide.ffi import can_run_sync, run_sync # noqa: F401 + + return bool(can_run_sync()) + except ImportError: + return False + + +def _is_node_js() -> bool: + """ + Check if we are in Node.js. + + :return: True if we are in Node.js. + :rtype: bool + """ + return ( + hasattr(js, "process") + and hasattr(js.process, "release") + # According to the Node.js documentation, the release name is always "node". + and js.process.release.name == "node" + ) + + +def streaming_ready() -> bool | None: + if _fetcher: + return _fetcher.streaming_ready + else: + return None # no fetcher, return None to signify that + + +async def wait_for_streaming_ready() -> bool: + if _fetcher: + await _fetcher.js_worker_ready_promise + return True + else: + return False diff --git a/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/request.py b/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/request.py new file mode 100644 index 0000000000000000000000000000000000000000..e692e692bd0d38f6a0677992a6993fc68050dff3 --- /dev/null +++ b/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/request.py @@ -0,0 +1,22 @@ +from __future__ import annotations + +from dataclasses import dataclass, field + +from ..._base_connection import _TYPE_BODY + + +@dataclass +class EmscriptenRequest: + method: str + url: str + params: dict[str, str] | None = None + body: _TYPE_BODY | None = None + headers: dict[str, str] = field(default_factory=dict) + timeout: float = 0 + decode_content: bool = True + + def set_header(self, name: str, value: str) -> None: + self.headers[name.capitalize()] = value + + def set_body(self, body: _TYPE_BODY | None) -> None: + self.body = body diff --git a/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/response.py b/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/response.py new file mode 100644 index 0000000000000000000000000000000000000000..cb1088a1826d089e1b603c51e85560b8583a3e3d --- /dev/null +++ b/venv/lib/python3.13/site-packages/urllib3/contrib/emscripten/response.py @@ -0,0 +1,277 @@ +from __future__ import annotations + +import json as _json +import logging +import typing +from contextlib import contextmanager +from dataclasses import dataclass +from http.client import HTTPException as HTTPException +from io import BytesIO, IOBase + +from ...exceptions import InvalidHeader, TimeoutError +from ...response import BaseHTTPResponse +from ...util.retry import Retry +from .request import EmscriptenRequest + +if typing.TYPE_CHECKING: + from ..._base_connection import BaseHTTPConnection, BaseHTTPSConnection + +log = logging.getLogger(__name__) + + +@dataclass +class EmscriptenResponse: + status_code: int + headers: dict[str, str] + body: IOBase | bytes + request: EmscriptenRequest + + +class EmscriptenHttpResponseWrapper(BaseHTTPResponse): + def __init__( + self, + internal_response: EmscriptenResponse, + url: str | None = None, + connection: BaseHTTPConnection | BaseHTTPSConnection | None = None, + ): + self._pool = None # set by pool class + self._body = None + self._response = internal_response + self._url = url + self._connection = connection + self._closed = False + super().__init__( + headers=internal_response.headers, + status=internal_response.status_code, + request_url=url, + version=0, + version_string="HTTP/?", + reason="", + decode_content=True, + ) + self.length_remaining = self._init_length(self._response.request.method) + self.length_is_certain = False + + @property + def url(self) -> str | None: + return self._url + + @url.setter + def url(self, url: str | None) -> None: + self._url = url + + @property + def connection(self) -> BaseHTTPConnection | BaseHTTPSConnection | None: + return self._connection + + @property + def retries(self) -> Retry | None: + return self._retries + + @retries.setter + def retries(self, retries: Retry | None) -> None: + # Override the request_url if retries has a redirect location. + self._retries = retries + + def stream( + self, amt: int | None = 2**16, decode_content: bool | None = None + ) -> typing.Generator[bytes]: + """ + A generator wrapper for the read() method. A call will block until + ``amt`` bytes have been read from the connection or until the + connection is closed. + + :param amt: + How much of the content to read. The generator will return up to + much data per iteration, but may return less. This is particularly + likely when using compressed data. However, the empty string will + never be returned. + + :param decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + """ + while True: + data = self.read(amt=amt, decode_content=decode_content) + + if data: + yield data + else: + break + + def _init_length(self, request_method: str | None) -> int | None: + length: int | None + content_length: str | None = self.headers.get("content-length") + + if content_length is not None: + try: + # RFC 7230 section 3.3.2 specifies multiple content lengths can + # be sent in a single Content-Length header + # (e.g. Content-Length: 42, 42). This line ensures the values + # are all valid ints and that as long as the `set` length is 1, + # all values are the same. Otherwise, the header is invalid. + lengths = {int(val) for val in content_length.split(",")} + if len(lengths) > 1: + raise InvalidHeader( + "Content-Length contained multiple " + "unmatching values (%s)" % content_length + ) + length = lengths.pop() + except ValueError: + length = None + else: + if length < 0: + length = None + + else: # if content_length is None + length = None + + # Check for responses that shouldn't include a body + if ( + self.status in (204, 304) + or 100 <= self.status < 200 + or request_method == "HEAD" + ): + length = 0 + + return length + + def read( + self, + amt: int | None = None, + decode_content: bool | None = None, # ignored because browser decodes always + cache_content: bool = False, + ) -> bytes: + if ( + self._closed + or self._response is None + or (isinstance(self._response.body, IOBase) and self._response.body.closed) + ): + return b"" + + with self._error_catcher(): + # body has been preloaded as a string by XmlHttpRequest + if not isinstance(self._response.body, IOBase): + self.length_remaining = len(self._response.body) + self.length_is_certain = True + # wrap body in IOStream + self._response.body = BytesIO(self._response.body) + if amt is not None and amt >= 0: + # don't cache partial content + cache_content = False + data = self._response.body.read(amt) + else: # read all we can (and cache it) + data = self._response.body.read() + if cache_content: + self._body = data + if self.length_remaining is not None: + self.length_remaining = max(self.length_remaining - len(data), 0) + if len(data) == 0 or ( + self.length_is_certain and self.length_remaining == 0 + ): + # definitely finished reading, close response stream + self._response.body.close() + return typing.cast(bytes, data) + + def read_chunked( + self, + amt: int | None = None, + decode_content: bool | None = None, + ) -> typing.Generator[bytes]: + # chunked is handled by browser + while True: + bytes = self.read(amt, decode_content) + if not bytes: + break + yield bytes + + def release_conn(self) -> None: + if not self._pool or not self._connection: + return None + + self._pool._put_conn(self._connection) + self._connection = None + + def drain_conn(self) -> None: + self.close() + + @property + def data(self) -> bytes: + if self._body: + return self._body + else: + return self.read(cache_content=True) + + def json(self) -> typing.Any: + """ + Deserializes the body of the HTTP response as a Python object. + + The body of the HTTP response must be encoded using UTF-8, as per + `RFC 8529 Section 8.1 `_. + + To use a custom JSON decoder pass the result of :attr:`HTTPResponse.data` to + your custom decoder instead. + + If the body of the HTTP response is not decodable to UTF-8, a + `UnicodeDecodeError` will be raised. If the body of the HTTP response is not a + valid JSON document, a `json.JSONDecodeError` will be raised. + + Read more :ref:`here `. + + :returns: The body of the HTTP response as a Python object. + """ + data = self.data.decode("utf-8") + return _json.loads(data) + + def close(self) -> None: + if not self._closed: + if isinstance(self._response.body, IOBase): + self._response.body.close() + if self._connection: + self._connection.close() + self._connection = None + self._closed = True + + @contextmanager + def _error_catcher(self) -> typing.Generator[None]: + """ + Catch Emscripten specific exceptions thrown by fetch.py, + instead re-raising urllib3 variants, so that low-level exceptions + are not leaked in the high-level api. + + On exit, release the connection back to the pool. + """ + from .fetch import _RequestError, _TimeoutError # avoid circular import + + clean_exit = False + + try: + yield + # If no exception is thrown, we should avoid cleaning up + # unnecessarily. + clean_exit = True + except _TimeoutError as e: + raise TimeoutError(str(e)) + except _RequestError as e: + raise HTTPException(str(e)) + finally: + # If we didn't terminate cleanly, we need to throw away our + # connection. + if not clean_exit: + # The response may not be closed but we're not going to use it + # anymore so close it now + if ( + isinstance(self._response.body, IOBase) + and not self._response.body.closed + ): + self._response.body.close() + # release the connection back to the pool + self.release_conn() + else: + # If we have read everything from the response stream, + # return the connection back to the pool. + if ( + isinstance(self._response.body, IOBase) + and self._response.body.closed + ): + self.release_conn() diff --git a/venv/lib/python3.13/site-packages/urllib3/contrib/pyopenssl.py b/venv/lib/python3.13/site-packages/urllib3/contrib/pyopenssl.py new file mode 100644 index 0000000000000000000000000000000000000000..3714500ec2e1c104b92dff1bb8f7244b8b458ed8 --- /dev/null +++ b/venv/lib/python3.13/site-packages/urllib3/contrib/pyopenssl.py @@ -0,0 +1,564 @@ +""" +Module for using pyOpenSSL as a TLS backend. This module was relevant before +the standard library ``ssl`` module supported SNI, but now that we've dropped +support for Python 2.7 all relevant Python versions support SNI so +**this module is no longer recommended**. + +This needs the following packages installed: + +* `pyOpenSSL`_ (tested with 16.0.0) +* `cryptography`_ (minimum 1.3.4, from pyopenssl) +* `idna`_ (minimum 2.0) + +However, pyOpenSSL depends on cryptography, so while we use all three directly here we +end up having relatively few packages required. + +You can install them with the following command: + +.. code-block:: bash + + $ python -m pip install pyopenssl cryptography idna + +To activate certificate checking, call +:func:`~urllib3.contrib.pyopenssl.inject_into_urllib3` from your Python code +before you begin making HTTP requests. This can be done in a ``sitecustomize`` +module, or at any other time before your application begins using ``urllib3``, +like this: + +.. code-block:: python + + try: + import urllib3.contrib.pyopenssl + urllib3.contrib.pyopenssl.inject_into_urllib3() + except ImportError: + pass + +.. _pyopenssl: https://www.pyopenssl.org +.. _cryptography: https://cryptography.io +.. _idna: https://github.com/kjd/idna +""" + +from __future__ import annotations + +import OpenSSL.SSL # type: ignore[import-untyped] +from cryptography import x509 + +try: + from cryptography.x509 import UnsupportedExtension # type: ignore[attr-defined] +except ImportError: + # UnsupportedExtension is gone in cryptography >= 2.1.0 + class UnsupportedExtension(Exception): # type: ignore[no-redef] + pass + + +import logging +import ssl +import typing +from io import BytesIO +from socket import socket as socket_cls +from socket import timeout + +from .. import util + +if typing.TYPE_CHECKING: + from OpenSSL.crypto import X509 # type: ignore[import-untyped] + + +__all__ = ["inject_into_urllib3", "extract_from_urllib3"] + +# Map from urllib3 to PyOpenSSL compatible parameter-values. +_openssl_versions: dict[int, int] = { + util.ssl_.PROTOCOL_TLS: OpenSSL.SSL.SSLv23_METHOD, # type: ignore[attr-defined] + util.ssl_.PROTOCOL_TLS_CLIENT: OpenSSL.SSL.SSLv23_METHOD, # type: ignore[attr-defined] + ssl.PROTOCOL_TLSv1: OpenSSL.SSL.TLSv1_METHOD, +} + +if hasattr(ssl, "PROTOCOL_TLSv1_1") and hasattr(OpenSSL.SSL, "TLSv1_1_METHOD"): + _openssl_versions[ssl.PROTOCOL_TLSv1_1] = OpenSSL.SSL.TLSv1_1_METHOD + +if hasattr(ssl, "PROTOCOL_TLSv1_2") and hasattr(OpenSSL.SSL, "TLSv1_2_METHOD"): + _openssl_versions[ssl.PROTOCOL_TLSv1_2] = OpenSSL.SSL.TLSv1_2_METHOD + + +_stdlib_to_openssl_verify = { + ssl.CERT_NONE: OpenSSL.SSL.VERIFY_NONE, + ssl.CERT_OPTIONAL: OpenSSL.SSL.VERIFY_PEER, + ssl.CERT_REQUIRED: OpenSSL.SSL.VERIFY_PEER + + OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT, +} +_openssl_to_stdlib_verify = {v: k for k, v in _stdlib_to_openssl_verify.items()} + +# The SSLvX values are the most likely to be missing in the future +# but we check them all just to be sure. +_OP_NO_SSLv2_OR_SSLv3: int = getattr(OpenSSL.SSL, "OP_NO_SSLv2", 0) | getattr( + OpenSSL.SSL, "OP_NO_SSLv3", 0 +) +_OP_NO_TLSv1: int = getattr(OpenSSL.SSL, "OP_NO_TLSv1", 0) +_OP_NO_TLSv1_1: int = getattr(OpenSSL.SSL, "OP_NO_TLSv1_1", 0) +_OP_NO_TLSv1_2: int = getattr(OpenSSL.SSL, "OP_NO_TLSv1_2", 0) +_OP_NO_TLSv1_3: int = getattr(OpenSSL.SSL, "OP_NO_TLSv1_3", 0) + +_openssl_to_ssl_minimum_version: dict[int, int] = { + ssl.TLSVersion.MINIMUM_SUPPORTED: _OP_NO_SSLv2_OR_SSLv3, + ssl.TLSVersion.TLSv1: _OP_NO_SSLv2_OR_SSLv3, + ssl.TLSVersion.TLSv1_1: _OP_NO_SSLv2_OR_SSLv3 | _OP_NO_TLSv1, + ssl.TLSVersion.TLSv1_2: _OP_NO_SSLv2_OR_SSLv3 | _OP_NO_TLSv1 | _OP_NO_TLSv1_1, + ssl.TLSVersion.TLSv1_3: ( + _OP_NO_SSLv2_OR_SSLv3 | _OP_NO_TLSv1 | _OP_NO_TLSv1_1 | _OP_NO_TLSv1_2 + ), + ssl.TLSVersion.MAXIMUM_SUPPORTED: ( + _OP_NO_SSLv2_OR_SSLv3 | _OP_NO_TLSv1 | _OP_NO_TLSv1_1 | _OP_NO_TLSv1_2 + ), +} +_openssl_to_ssl_maximum_version: dict[int, int] = { + ssl.TLSVersion.MINIMUM_SUPPORTED: ( + _OP_NO_SSLv2_OR_SSLv3 + | _OP_NO_TLSv1 + | _OP_NO_TLSv1_1 + | _OP_NO_TLSv1_2 + | _OP_NO_TLSv1_3 + ), + ssl.TLSVersion.TLSv1: ( + _OP_NO_SSLv2_OR_SSLv3 | _OP_NO_TLSv1_1 | _OP_NO_TLSv1_2 | _OP_NO_TLSv1_3 + ), + ssl.TLSVersion.TLSv1_1: _OP_NO_SSLv2_OR_SSLv3 | _OP_NO_TLSv1_2 | _OP_NO_TLSv1_3, + ssl.TLSVersion.TLSv1_2: _OP_NO_SSLv2_OR_SSLv3 | _OP_NO_TLSv1_3, + ssl.TLSVersion.TLSv1_3: _OP_NO_SSLv2_OR_SSLv3, + ssl.TLSVersion.MAXIMUM_SUPPORTED: _OP_NO_SSLv2_OR_SSLv3, +} + +# OpenSSL will only write 16K at a time +SSL_WRITE_BLOCKSIZE = 16384 + +orig_util_SSLContext = util.ssl_.SSLContext + + +log = logging.getLogger(__name__) + + +def inject_into_urllib3() -> None: + "Monkey-patch urllib3 with PyOpenSSL-backed SSL-support." + + _validate_dependencies_met() + + util.SSLContext = PyOpenSSLContext # type: ignore[assignment] + util.ssl_.SSLContext = PyOpenSSLContext # type: ignore[assignment] + util.IS_PYOPENSSL = True + util.ssl_.IS_PYOPENSSL = True + + +def extract_from_urllib3() -> None: + "Undo monkey-patching by :func:`inject_into_urllib3`." + + util.SSLContext = orig_util_SSLContext + util.ssl_.SSLContext = orig_util_SSLContext + util.IS_PYOPENSSL = False + util.ssl_.IS_PYOPENSSL = False + + +def _validate_dependencies_met() -> None: + """ + Verifies that PyOpenSSL's package-level dependencies have been met. + Throws `ImportError` if they are not met. + """ + # Method added in `cryptography==1.1`; not available in older versions + from cryptography.x509.extensions import Extensions + + if getattr(Extensions, "get_extension_for_class", None) is None: + raise ImportError( + "'cryptography' module missing required functionality. " + "Try upgrading to v1.3.4 or newer." + ) + + # pyOpenSSL 0.14 and above use cryptography for OpenSSL bindings. The _x509 + # attribute is only present on those versions. + from OpenSSL.crypto import X509 + + x509 = X509() + if getattr(x509, "_x509", None) is None: + raise ImportError( + "'pyOpenSSL' module missing required functionality. " + "Try upgrading to v0.14 or newer." + ) + + +def _dnsname_to_stdlib(name: str) -> str | None: + """ + Converts a dNSName SubjectAlternativeName field to the form used by the + standard library on the given Python version. + + Cryptography produces a dNSName as a unicode string that was idna-decoded + from ASCII bytes. We need to idna-encode that string to get it back, and + then on Python 3 we also need to convert to unicode via UTF-8 (the stdlib + uses PyUnicode_FromStringAndSize on it, which decodes via UTF-8). + + If the name cannot be idna-encoded then we return None signalling that + the name given should be skipped. + """ + + def idna_encode(name: str) -> bytes | None: + """ + Borrowed wholesale from the Python Cryptography Project. It turns out + that we can't just safely call `idna.encode`: it can explode for + wildcard names. This avoids that problem. + """ + import idna + + try: + for prefix in ["*.", "."]: + if name.startswith(prefix): + name = name[len(prefix) :] + return prefix.encode("ascii") + idna.encode(name) + return idna.encode(name) + except idna.core.IDNAError: + return None + + # Don't send IPv6 addresses through the IDNA encoder. + if ":" in name: + return name + + encoded_name = idna_encode(name) + if encoded_name is None: + return None + return encoded_name.decode("utf-8") + + +def get_subj_alt_name(peer_cert: X509) -> list[tuple[str, str]]: + """ + Given an PyOpenSSL certificate, provides all the subject alternative names. + """ + cert = peer_cert.to_cryptography() + + # We want to find the SAN extension. Ask Cryptography to locate it (it's + # faster than looping in Python) + try: + ext = cert.extensions.get_extension_for_class(x509.SubjectAlternativeName).value + except x509.ExtensionNotFound: + # No such extension, return the empty list. + return [] + except ( + x509.DuplicateExtension, + UnsupportedExtension, + x509.UnsupportedGeneralNameType, + UnicodeError, + ) as e: + # A problem has been found with the quality of the certificate. Assume + # no SAN field is present. + log.warning( + "A problem was encountered with the certificate that prevented " + "urllib3 from finding the SubjectAlternativeName field. This can " + "affect certificate validation. The error was %s", + e, + ) + return [] + + # We want to return dNSName and iPAddress fields. We need to cast the IPs + # back to strings because the match_hostname function wants them as + # strings. + # Sadly the DNS names need to be idna encoded and then, on Python 3, UTF-8 + # decoded. This is pretty frustrating, but that's what the standard library + # does with certificates, and so we need to attempt to do the same. + # We also want to skip over names which cannot be idna encoded. + names = [ + ("DNS", name) + for name in map(_dnsname_to_stdlib, ext.get_values_for_type(x509.DNSName)) + if name is not None + ] + names.extend( + ("IP Address", str(name)) for name in ext.get_values_for_type(x509.IPAddress) + ) + + return names + + +class WrappedSocket: + """API-compatibility wrapper for Python OpenSSL's Connection-class.""" + + def __init__( + self, + connection: OpenSSL.SSL.Connection, + socket: socket_cls, + suppress_ragged_eofs: bool = True, + ) -> None: + self.connection = connection + self.socket = socket + self.suppress_ragged_eofs = suppress_ragged_eofs + self._io_refs = 0 + self._closed = False + + def fileno(self) -> int: + return self.socket.fileno() + + # Copy-pasted from Python 3.5 source code + def _decref_socketios(self) -> None: + if self._io_refs > 0: + self._io_refs -= 1 + if self._closed: + self.close() + + def recv(self, *args: typing.Any, **kwargs: typing.Any) -> bytes: + try: + data = self.connection.recv(*args, **kwargs) + except OpenSSL.SSL.SysCallError as e: + if self.suppress_ragged_eofs and e.args == (-1, "Unexpected EOF"): + return b"" + else: + raise OSError(e.args[0], str(e)) from e + except OpenSSL.SSL.ZeroReturnError: + if self.connection.get_shutdown() == OpenSSL.SSL.RECEIVED_SHUTDOWN: + return b"" + else: + raise + except OpenSSL.SSL.WantReadError as e: + if not util.wait_for_read(self.socket, self.socket.gettimeout()): + raise timeout("The read operation timed out") from e + else: + return self.recv(*args, **kwargs) + + # TLS 1.3 post-handshake authentication + except OpenSSL.SSL.Error as e: + raise ssl.SSLError(f"read error: {e!r}") from e + else: + return data # type: ignore[no-any-return] + + def recv_into(self, *args: typing.Any, **kwargs: typing.Any) -> int: + try: + return self.connection.recv_into(*args, **kwargs) # type: ignore[no-any-return] + except OpenSSL.SSL.SysCallError as e: + if self.suppress_ragged_eofs and e.args == (-1, "Unexpected EOF"): + return 0 + else: + raise OSError(e.args[0], str(e)) from e + except OpenSSL.SSL.ZeroReturnError: + if self.connection.get_shutdown() == OpenSSL.SSL.RECEIVED_SHUTDOWN: + return 0 + else: + raise + except OpenSSL.SSL.WantReadError as e: + if not util.wait_for_read(self.socket, self.socket.gettimeout()): + raise timeout("The read operation timed out") from e + else: + return self.recv_into(*args, **kwargs) + + # TLS 1.3 post-handshake authentication + except OpenSSL.SSL.Error as e: + raise ssl.SSLError(f"read error: {e!r}") from e + + def settimeout(self, timeout: float) -> None: + return self.socket.settimeout(timeout) + + def _send_until_done(self, data: bytes) -> int: + while True: + try: + return self.connection.send(data) # type: ignore[no-any-return] + except OpenSSL.SSL.WantWriteError as e: + if not util.wait_for_write(self.socket, self.socket.gettimeout()): + raise timeout() from e + continue + except OpenSSL.SSL.SysCallError as e: + raise OSError(e.args[0], str(e)) from e + + def sendall(self, data: bytes) -> None: + total_sent = 0 + while total_sent < len(data): + sent = self._send_until_done( + data[total_sent : total_sent + SSL_WRITE_BLOCKSIZE] + ) + total_sent += sent + + def shutdown(self, how: int) -> None: + try: + self.connection.shutdown() + except OpenSSL.SSL.Error as e: + raise ssl.SSLError(f"shutdown error: {e!r}") from e + + def close(self) -> None: + self._closed = True + if self._io_refs <= 0: + self._real_close() + + def _real_close(self) -> None: + try: + return self.connection.close() # type: ignore[no-any-return] + except OpenSSL.SSL.Error: + return + + def getpeercert( + self, binary_form: bool = False + ) -> dict[str, list[typing.Any]] | None: + x509 = self.connection.get_peer_certificate() + + if not x509: + return x509 # type: ignore[no-any-return] + + if binary_form: + return OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_ASN1, x509) # type: ignore[no-any-return] + + return { + "subject": ((("commonName", x509.get_subject().CN),),), # type: ignore[dict-item] + "subjectAltName": get_subj_alt_name(x509), + } + + def version(self) -> str: + return self.connection.get_protocol_version_name() # type: ignore[no-any-return] + + def selected_alpn_protocol(self) -> str | None: + alpn_proto = self.connection.get_alpn_proto_negotiated() + return alpn_proto.decode() if alpn_proto else None + + +WrappedSocket.makefile = socket_cls.makefile # type: ignore[attr-defined] + + +class PyOpenSSLContext: + """ + I am a wrapper class for the PyOpenSSL ``Context`` object. I am responsible + for translating the interface of the standard library ``SSLContext`` object + to calls into PyOpenSSL. + """ + + def __init__(self, protocol: int) -> None: + self.protocol = _openssl_versions[protocol] + self._ctx = OpenSSL.SSL.Context(self.protocol) + self._options = 0 + self.check_hostname = False + self._minimum_version: int = ssl.TLSVersion.MINIMUM_SUPPORTED + self._maximum_version: int = ssl.TLSVersion.MAXIMUM_SUPPORTED + self._verify_flags: int = ssl.VERIFY_X509_TRUSTED_FIRST + + @property + def options(self) -> int: + return self._options + + @options.setter + def options(self, value: int) -> None: + self._options = value + self._set_ctx_options() + + @property + def verify_flags(self) -> int: + return self._verify_flags + + @verify_flags.setter + def verify_flags(self, value: int) -> None: + self._verify_flags = value + self._ctx.get_cert_store().set_flags(self._verify_flags) + + @property + def verify_mode(self) -> int: + return _openssl_to_stdlib_verify[self._ctx.get_verify_mode()] + + @verify_mode.setter + def verify_mode(self, value: ssl.VerifyMode) -> None: + self._ctx.set_verify(_stdlib_to_openssl_verify[value], _verify_callback) + + def set_default_verify_paths(self) -> None: + self._ctx.set_default_verify_paths() + + def set_ciphers(self, ciphers: bytes | str) -> None: + if isinstance(ciphers, str): + ciphers = ciphers.encode("utf-8") + self._ctx.set_cipher_list(ciphers) + + def load_verify_locations( + self, + cafile: str | None = None, + capath: str | None = None, + cadata: bytes | None = None, + ) -> None: + if cafile is not None: + cafile = cafile.encode("utf-8") # type: ignore[assignment] + if capath is not None: + capath = capath.encode("utf-8") # type: ignore[assignment] + try: + self._ctx.load_verify_locations(cafile, capath) + if cadata is not None: + self._ctx.load_verify_locations(BytesIO(cadata)) + except OpenSSL.SSL.Error as e: + raise ssl.SSLError(f"unable to load trusted certificates: {e!r}") from e + + def load_cert_chain( + self, + certfile: str, + keyfile: str | None = None, + password: str | None = None, + ) -> None: + try: + self._ctx.use_certificate_chain_file(certfile) + if password is not None: + if not isinstance(password, bytes): + password = password.encode("utf-8") # type: ignore[assignment] + self._ctx.set_passwd_cb(lambda *_: password) + self._ctx.use_privatekey_file(keyfile or certfile) + except OpenSSL.SSL.Error as e: + raise ssl.SSLError(f"Unable to load certificate chain: {e!r}") from e + + def set_alpn_protocols(self, protocols: list[bytes | str]) -> None: + protocols = [util.util.to_bytes(p, "ascii") for p in protocols] + return self._ctx.set_alpn_protos(protocols) # type: ignore[no-any-return] + + def wrap_socket( + self, + sock: socket_cls, + server_side: bool = False, + do_handshake_on_connect: bool = True, + suppress_ragged_eofs: bool = True, + server_hostname: bytes | str | None = None, + ) -> WrappedSocket: + cnx = OpenSSL.SSL.Connection(self._ctx, sock) + + # If server_hostname is an IP, don't use it for SNI, per RFC6066 Section 3 + if server_hostname and not util.ssl_.is_ipaddress(server_hostname): + if isinstance(server_hostname, str): + server_hostname = server_hostname.encode("utf-8") + cnx.set_tlsext_host_name(server_hostname) + + cnx.set_connect_state() + + while True: + try: + cnx.do_handshake() + except OpenSSL.SSL.WantReadError as e: + if not util.wait_for_read(sock, sock.gettimeout()): + raise timeout("select timed out") from e + continue + except OpenSSL.SSL.Error as e: + raise ssl.SSLError(f"bad handshake: {e!r}") from e + break + + return WrappedSocket(cnx, sock) + + def _set_ctx_options(self) -> None: + self._ctx.set_options( + self._options + | _openssl_to_ssl_minimum_version[self._minimum_version] + | _openssl_to_ssl_maximum_version[self._maximum_version] + ) + + @property + def minimum_version(self) -> int: + return self._minimum_version + + @minimum_version.setter + def minimum_version(self, minimum_version: int) -> None: + self._minimum_version = minimum_version + self._set_ctx_options() + + @property + def maximum_version(self) -> int: + return self._maximum_version + + @maximum_version.setter + def maximum_version(self, maximum_version: int) -> None: + self._maximum_version = maximum_version + self._set_ctx_options() + + +def _verify_callback( + cnx: OpenSSL.SSL.Connection, + x509: X509, + err_no: int, + err_depth: int, + return_code: int, +) -> bool: + return err_no == 0 diff --git a/venv/lib/python3.13/site-packages/urllib3/contrib/socks.py b/venv/lib/python3.13/site-packages/urllib3/contrib/socks.py new file mode 100644 index 0000000000000000000000000000000000000000..c62b5e0332e7004d3a20da1fd555033f6f88780b --- /dev/null +++ b/venv/lib/python3.13/site-packages/urllib3/contrib/socks.py @@ -0,0 +1,228 @@ +""" +This module contains provisional support for SOCKS proxies from within +urllib3. This module supports SOCKS4, SOCKS4A (an extension of SOCKS4), and +SOCKS5. To enable its functionality, either install PySocks or install this +module with the ``socks`` extra. + +The SOCKS implementation supports the full range of urllib3 features. It also +supports the following SOCKS features: + +- SOCKS4A (``proxy_url='socks4a://...``) +- SOCKS4 (``proxy_url='socks4://...``) +- SOCKS5 with remote DNS (``proxy_url='socks5h://...``) +- SOCKS5 with local DNS (``proxy_url='socks5://...``) +- Usernames and passwords for the SOCKS proxy + +.. note:: + It is recommended to use ``socks5h://`` or ``socks4a://`` schemes in + your ``proxy_url`` to ensure that DNS resolution is done from the remote + server instead of client-side when connecting to a domain name. + +SOCKS4 supports IPv4 and domain names with the SOCKS4A extension. SOCKS5 +supports IPv4, IPv6, and domain names. + +When connecting to a SOCKS4 proxy the ``username`` portion of the ``proxy_url`` +will be sent as the ``userid`` section of the SOCKS request: + +.. code-block:: python + + proxy_url="socks4a://@proxy-host" + +When connecting to a SOCKS5 proxy the ``username`` and ``password`` portion +of the ``proxy_url`` will be sent as the username/password to authenticate +with the proxy: + +.. code-block:: python + + proxy_url="socks5h://:@proxy-host" + +""" + +from __future__ import annotations + +try: + import socks # type: ignore[import-not-found] +except ImportError: + import warnings + + from ..exceptions import DependencyWarning + + warnings.warn( + ( + "SOCKS support in urllib3 requires the installation of optional " + "dependencies: specifically, PySocks. For more information, see " + "https://urllib3.readthedocs.io/en/latest/advanced-usage.html#socks-proxies" + ), + DependencyWarning, + ) + raise + +import typing +from socket import timeout as SocketTimeout + +from ..connection import HTTPConnection, HTTPSConnection +from ..connectionpool import HTTPConnectionPool, HTTPSConnectionPool +from ..exceptions import ConnectTimeoutError, NewConnectionError +from ..poolmanager import PoolManager +from ..util.url import parse_url + +try: + import ssl +except ImportError: + ssl = None # type: ignore[assignment] + + +class _TYPE_SOCKS_OPTIONS(typing.TypedDict): + socks_version: int + proxy_host: str | None + proxy_port: str | None + username: str | None + password: str | None + rdns: bool + + +class SOCKSConnection(HTTPConnection): + """ + A plain-text HTTP connection that connects via a SOCKS proxy. + """ + + def __init__( + self, + _socks_options: _TYPE_SOCKS_OPTIONS, + *args: typing.Any, + **kwargs: typing.Any, + ) -> None: + self._socks_options = _socks_options + super().__init__(*args, **kwargs) + + def _new_conn(self) -> socks.socksocket: + """ + Establish a new connection via the SOCKS proxy. + """ + extra_kw: dict[str, typing.Any] = {} + if self.source_address: + extra_kw["source_address"] = self.source_address + + if self.socket_options: + extra_kw["socket_options"] = self.socket_options + + try: + conn = socks.create_connection( + (self.host, self.port), + proxy_type=self._socks_options["socks_version"], + proxy_addr=self._socks_options["proxy_host"], + proxy_port=self._socks_options["proxy_port"], + proxy_username=self._socks_options["username"], + proxy_password=self._socks_options["password"], + proxy_rdns=self._socks_options["rdns"], + timeout=self.timeout, + **extra_kw, + ) + + except SocketTimeout as e: + raise ConnectTimeoutError( + self, + f"Connection to {self.host} timed out. (connect timeout={self.timeout})", + ) from e + + except socks.ProxyError as e: + # This is fragile as hell, but it seems to be the only way to raise + # useful errors here. + if e.socket_err: + error = e.socket_err + if isinstance(error, SocketTimeout): + raise ConnectTimeoutError( + self, + f"Connection to {self.host} timed out. (connect timeout={self.timeout})", + ) from e + else: + # Adding `from e` messes with coverage somehow, so it's omitted. + # See #2386. + raise NewConnectionError( + self, f"Failed to establish a new connection: {error}" + ) + else: + raise NewConnectionError( + self, f"Failed to establish a new connection: {e}" + ) from e + + except OSError as e: # Defensive: PySocks should catch all these. + raise NewConnectionError( + self, f"Failed to establish a new connection: {e}" + ) from e + + return conn + + +# We don't need to duplicate the Verified/Unverified distinction from +# urllib3/connection.py here because the HTTPSConnection will already have been +# correctly set to either the Verified or Unverified form by that module. This +# means the SOCKSHTTPSConnection will automatically be the correct type. +class SOCKSHTTPSConnection(SOCKSConnection, HTTPSConnection): + pass + + +class SOCKSHTTPConnectionPool(HTTPConnectionPool): + ConnectionCls = SOCKSConnection + + +class SOCKSHTTPSConnectionPool(HTTPSConnectionPool): + ConnectionCls = SOCKSHTTPSConnection + + +class SOCKSProxyManager(PoolManager): + """ + A version of the urllib3 ProxyManager that routes connections via the + defined SOCKS proxy. + """ + + pool_classes_by_scheme = { + "http": SOCKSHTTPConnectionPool, + "https": SOCKSHTTPSConnectionPool, + } + + def __init__( + self, + proxy_url: str, + username: str | None = None, + password: str | None = None, + num_pools: int = 10, + headers: typing.Mapping[str, str] | None = None, + **connection_pool_kw: typing.Any, + ): + parsed = parse_url(proxy_url) + + if username is None and password is None and parsed.auth is not None: + split = parsed.auth.split(":") + if len(split) == 2: + username, password = split + if parsed.scheme == "socks5": + socks_version = socks.PROXY_TYPE_SOCKS5 + rdns = False + elif parsed.scheme == "socks5h": + socks_version = socks.PROXY_TYPE_SOCKS5 + rdns = True + elif parsed.scheme == "socks4": + socks_version = socks.PROXY_TYPE_SOCKS4 + rdns = False + elif parsed.scheme == "socks4a": + socks_version = socks.PROXY_TYPE_SOCKS4 + rdns = True + else: + raise ValueError(f"Unable to determine SOCKS version from {proxy_url}") + + self.proxy_url = proxy_url + + socks_options = { + "socks_version": socks_version, + "proxy_host": parsed.host, + "proxy_port": parsed.port, + "username": username, + "password": password, + "rdns": rdns, + } + connection_pool_kw["_socks_options"] = socks_options + + super().__init__(num_pools, headers, **connection_pool_kw) + + self.pool_classes_by_scheme = SOCKSProxyManager.pool_classes_by_scheme diff --git a/venv/lib/python3.13/site-packages/urllib3/http2/__init__.py b/venv/lib/python3.13/site-packages/urllib3/http2/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..133e1d8f237f6fddd557ae1c0e0cf738f7cc2748 --- /dev/null +++ b/venv/lib/python3.13/site-packages/urllib3/http2/__init__.py @@ -0,0 +1,53 @@ +from __future__ import annotations + +from importlib.metadata import version + +__all__ = [ + "inject_into_urllib3", + "extract_from_urllib3", +] + +import typing + +orig_HTTPSConnection: typing.Any = None + + +def inject_into_urllib3() -> None: + # First check if h2 version is valid + h2_version = version("h2") + if not h2_version.startswith("4."): + raise ImportError( + "urllib3 v2 supports h2 version 4.x.x, currently " + f"the 'h2' module is compiled with {h2_version!r}. " + "See: https://github.com/urllib3/urllib3/issues/3290" + ) + + # Import here to avoid circular dependencies. + from .. import connection as urllib3_connection + from .. import util as urllib3_util + from ..connectionpool import HTTPSConnectionPool + from ..util import ssl_ as urllib3_util_ssl + from .connection import HTTP2Connection + + global orig_HTTPSConnection + orig_HTTPSConnection = urllib3_connection.HTTPSConnection + + HTTPSConnectionPool.ConnectionCls = HTTP2Connection + urllib3_connection.HTTPSConnection = HTTP2Connection # type: ignore[misc] + + # TODO: Offer 'http/1.1' as well, but for testing purposes this is handy. + urllib3_util.ALPN_PROTOCOLS = ["h2"] + urllib3_util_ssl.ALPN_PROTOCOLS = ["h2"] + + +def extract_from_urllib3() -> None: + from .. import connection as urllib3_connection + from .. import util as urllib3_util + from ..connectionpool import HTTPSConnectionPool + from ..util import ssl_ as urllib3_util_ssl + + HTTPSConnectionPool.ConnectionCls = orig_HTTPSConnection + urllib3_connection.HTTPSConnection = orig_HTTPSConnection # type: ignore[misc] + + urllib3_util.ALPN_PROTOCOLS = ["http/1.1"] + urllib3_util_ssl.ALPN_PROTOCOLS = ["http/1.1"] diff --git a/venv/lib/python3.13/site-packages/urllib3/http2/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/http2/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6369a20ec51b0030c140b8dd5852966181c86752 Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/http2/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/http2/__pycache__/connection.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/http2/__pycache__/connection.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..64098731d466a5566c6f19bc71c21971d4ad546f Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/http2/__pycache__/connection.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/http2/__pycache__/probe.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/http2/__pycache__/probe.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8195153af076d90f3f5228d42a98bd3f7f31a528 Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/http2/__pycache__/probe.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/http2/connection.py b/venv/lib/python3.13/site-packages/urllib3/http2/connection.py new file mode 100644 index 0000000000000000000000000000000000000000..d0822391f6f6bd1c1203fd0c212100e7144c3553 --- /dev/null +++ b/venv/lib/python3.13/site-packages/urllib3/http2/connection.py @@ -0,0 +1,356 @@ +from __future__ import annotations + +import logging +import re +import threading +import types +import typing + +import h2.config # type: ignore[import-untyped] +import h2.connection # type: ignore[import-untyped] +import h2.events # type: ignore[import-untyped] + +from .._base_connection import _TYPE_BODY +from .._collections import HTTPHeaderDict +from ..connection import HTTPSConnection, _get_default_user_agent +from ..exceptions import ConnectionError +from ..response import BaseHTTPResponse + +orig_HTTPSConnection = HTTPSConnection + +T = typing.TypeVar("T") + +log = logging.getLogger(__name__) + +RE_IS_LEGAL_HEADER_NAME = re.compile(rb"^[!#$%&'*+\-.^_`|~0-9a-z]+$") +RE_IS_ILLEGAL_HEADER_VALUE = re.compile(rb"[\0\x00\x0a\x0d\r\n]|^[ \r\n\t]|[ \r\n\t]$") + + +def _is_legal_header_name(name: bytes) -> bool: + """ + "An implementation that validates fields according to the definitions in Sections + 5.1 and 5.5 of [HTTP] only needs an additional check that field names do not + include uppercase characters." (https://httpwg.org/specs/rfc9113.html#n-field-validity) + + `http.client._is_legal_header_name` does not validate the field name according to the + HTTP 1.1 spec, so we do that here, in addition to checking for uppercase characters. + + This does not allow for the `:` character in the header name, so should not + be used to validate pseudo-headers. + """ + return bool(RE_IS_LEGAL_HEADER_NAME.match(name)) + + +def _is_illegal_header_value(value: bytes) -> bool: + """ + "A field value MUST NOT contain the zero value (ASCII NUL, 0x00), line feed + (ASCII LF, 0x0a), or carriage return (ASCII CR, 0x0d) at any position. A field + value MUST NOT start or end with an ASCII whitespace character (ASCII SP or HTAB, + 0x20 or 0x09)." (https://httpwg.org/specs/rfc9113.html#n-field-validity) + """ + return bool(RE_IS_ILLEGAL_HEADER_VALUE.search(value)) + + +class _LockedObject(typing.Generic[T]): + """ + A wrapper class that hides a specific object behind a lock. + The goal here is to provide a simple way to protect access to an object + that cannot safely be simultaneously accessed from multiple threads. The + intended use of this class is simple: take hold of it with a context + manager, which returns the protected object. + """ + + __slots__ = ( + "lock", + "_obj", + ) + + def __init__(self, obj: T): + self.lock = threading.RLock() + self._obj = obj + + def __enter__(self) -> T: + self.lock.acquire() + return self._obj + + def __exit__( + self, + exc_type: type[BaseException] | None, + exc_val: BaseException | None, + exc_tb: types.TracebackType | None, + ) -> None: + self.lock.release() + + +class HTTP2Connection(HTTPSConnection): + def __init__( + self, host: str, port: int | None = None, **kwargs: typing.Any + ) -> None: + self._h2_conn = self._new_h2_conn() + self._h2_stream: int | None = None + self._headers: list[tuple[bytes, bytes]] = [] + + if "proxy" in kwargs or "proxy_config" in kwargs: # Defensive: + raise NotImplementedError("Proxies aren't supported with HTTP/2") + + super().__init__(host, port, **kwargs) + + if self._tunnel_host is not None: + raise NotImplementedError("Tunneling isn't supported with HTTP/2") + + def _new_h2_conn(self) -> _LockedObject[h2.connection.H2Connection]: + config = h2.config.H2Configuration(client_side=True) + return _LockedObject(h2.connection.H2Connection(config=config)) + + def connect(self) -> None: + super().connect() + with self._h2_conn as conn: + conn.initiate_connection() + if data_to_send := conn.data_to_send(): + self.sock.sendall(data_to_send) + + def putrequest( # type: ignore[override] + self, + method: str, + url: str, + **kwargs: typing.Any, + ) -> None: + """putrequest + This deviates from the HTTPConnection method signature since we never need to override + sending accept-encoding headers or the host header. + """ + if "skip_host" in kwargs: + raise NotImplementedError("`skip_host` isn't supported") + if "skip_accept_encoding" in kwargs: + raise NotImplementedError("`skip_accept_encoding` isn't supported") + + self._request_url = url or "/" + self._validate_path(url) # type: ignore[attr-defined] + + if ":" in self.host: + authority = f"[{self.host}]:{self.port or 443}" + else: + authority = f"{self.host}:{self.port or 443}" + + self._headers.append((b":scheme", b"https")) + self._headers.append((b":method", method.encode())) + self._headers.append((b":authority", authority.encode())) + self._headers.append((b":path", url.encode())) + + with self._h2_conn as conn: + self._h2_stream = conn.get_next_available_stream_id() + + def putheader(self, header: str | bytes, *values: str | bytes) -> None: # type: ignore[override] + # TODO SKIPPABLE_HEADERS from urllib3 are ignored. + header = header.encode() if isinstance(header, str) else header + header = header.lower() # A lot of upstream code uses capitalized headers. + if not _is_legal_header_name(header): + raise ValueError(f"Illegal header name {str(header)}") + + for value in values: + value = value.encode() if isinstance(value, str) else value + if _is_illegal_header_value(value): + raise ValueError(f"Illegal header value {str(value)}") + self._headers.append((header, value)) + + def endheaders(self, message_body: typing.Any = None) -> None: # type: ignore[override] + if self._h2_stream is None: + raise ConnectionError("Must call `putrequest` first.") + + with self._h2_conn as conn: + conn.send_headers( + stream_id=self._h2_stream, + headers=self._headers, + end_stream=(message_body is None), + ) + if data_to_send := conn.data_to_send(): + self.sock.sendall(data_to_send) + self._headers = [] # Reset headers for the next request. + + def send(self, data: typing.Any) -> None: + """Send data to the server. + `data` can be: `str`, `bytes`, an iterable, or file-like objects + that support a .read() method. + """ + if self._h2_stream is None: + raise ConnectionError("Must call `putrequest` first.") + + with self._h2_conn as conn: + if data_to_send := conn.data_to_send(): + self.sock.sendall(data_to_send) + + if hasattr(data, "read"): # file-like objects + while True: + chunk = data.read(self.blocksize) + if not chunk: + break + if isinstance(chunk, str): + chunk = chunk.encode() # pragma: no cover + conn.send_data(self._h2_stream, chunk, end_stream=False) + if data_to_send := conn.data_to_send(): + self.sock.sendall(data_to_send) + conn.end_stream(self._h2_stream) + return + + if isinstance(data, str): # str -> bytes + data = data.encode() + + try: + if isinstance(data, bytes): + conn.send_data(self._h2_stream, data, end_stream=True) + if data_to_send := conn.data_to_send(): + self.sock.sendall(data_to_send) + else: + for chunk in data: + conn.send_data(self._h2_stream, chunk, end_stream=False) + if data_to_send := conn.data_to_send(): + self.sock.sendall(data_to_send) + conn.end_stream(self._h2_stream) + except TypeError: + raise TypeError( + "`data` should be str, bytes, iterable, or file. got %r" + % type(data) + ) + + def set_tunnel( + self, + host: str, + port: int | None = None, + headers: typing.Mapping[str, str] | None = None, + scheme: str = "http", + ) -> None: + raise NotImplementedError( + "HTTP/2 does not support setting up a tunnel through a proxy" + ) + + def getresponse( # type: ignore[override] + self, + ) -> HTTP2Response: + status = None + data = bytearray() + with self._h2_conn as conn: + end_stream = False + while not end_stream: + # TODO: Arbitrary read value. + if received_data := self.sock.recv(65535): + events = conn.receive_data(received_data) + for event in events: + if isinstance(event, h2.events.ResponseReceived): + headers = HTTPHeaderDict() + for header, value in event.headers: + if header == b":status": + status = int(value.decode()) + else: + headers.add( + header.decode("ascii"), value.decode("ascii") + ) + + elif isinstance(event, h2.events.DataReceived): + data += event.data + conn.acknowledge_received_data( + event.flow_controlled_length, event.stream_id + ) + + elif isinstance(event, h2.events.StreamEnded): + end_stream = True + + if data_to_send := conn.data_to_send(): + self.sock.sendall(data_to_send) + + assert status is not None + return HTTP2Response( + status=status, + headers=headers, + request_url=self._request_url, + data=bytes(data), + ) + + def request( # type: ignore[override] + self, + method: str, + url: str, + body: _TYPE_BODY | None = None, + headers: typing.Mapping[str, str] | None = None, + *, + preload_content: bool = True, + decode_content: bool = True, + enforce_content_length: bool = True, + **kwargs: typing.Any, + ) -> None: + """Send an HTTP/2 request""" + if "chunked" in kwargs: + # TODO this is often present from upstream. + # raise NotImplementedError("`chunked` isn't supported with HTTP/2") + pass + + if self.sock is not None: + self.sock.settimeout(self.timeout) + + self.putrequest(method, url) + + headers = headers or {} + for k, v in headers.items(): + if k.lower() == "transfer-encoding" and v == "chunked": + continue + else: + self.putheader(k, v) + + if b"user-agent" not in dict(self._headers): + self.putheader(b"user-agent", _get_default_user_agent()) + + if body: + self.endheaders(message_body=body) + self.send(body) + else: + self.endheaders() + + def close(self) -> None: + with self._h2_conn as conn: + try: + conn.close_connection() + if data := conn.data_to_send(): + self.sock.sendall(data) + except Exception: + pass + + # Reset all our HTTP/2 connection state. + self._h2_conn = self._new_h2_conn() + self._h2_stream = None + self._headers = [] + + super().close() + + +class HTTP2Response(BaseHTTPResponse): + # TODO: This is a woefully incomplete response object, but works for non-streaming. + def __init__( + self, + status: int, + headers: HTTPHeaderDict, + request_url: str, + data: bytes, + decode_content: bool = False, # TODO: support decoding + ) -> None: + super().__init__( + status=status, + headers=headers, + # Following CPython, we map HTTP versions to major * 10 + minor integers + version=20, + version_string="HTTP/2", + # No reason phrase in HTTP/2 + reason=None, + decode_content=decode_content, + request_url=request_url, + ) + self._data = data + self.length_remaining = 0 + + @property + def data(self) -> bytes: + return self._data + + def get_redirect_location(self) -> None: + return None + + def close(self) -> None: + pass diff --git a/venv/lib/python3.13/site-packages/urllib3/http2/probe.py b/venv/lib/python3.13/site-packages/urllib3/http2/probe.py new file mode 100644 index 0000000000000000000000000000000000000000..9ea900764f0885eafaac9454523417d86e33df2d --- /dev/null +++ b/venv/lib/python3.13/site-packages/urllib3/http2/probe.py @@ -0,0 +1,87 @@ +from __future__ import annotations + +import threading + + +class _HTTP2ProbeCache: + __slots__ = ( + "_lock", + "_cache_locks", + "_cache_values", + ) + + def __init__(self) -> None: + self._lock = threading.Lock() + self._cache_locks: dict[tuple[str, int], threading.RLock] = {} + self._cache_values: dict[tuple[str, int], bool | None] = {} + + def acquire_and_get(self, host: str, port: int) -> bool | None: + # By the end of this block we know that + # _cache_[values,locks] is available. + value = None + with self._lock: + key = (host, port) + try: + value = self._cache_values[key] + # If it's a known value we return right away. + if value is not None: + return value + except KeyError: + self._cache_locks[key] = threading.RLock() + self._cache_values[key] = None + + # If the value is unknown, we acquire the lock to signal + # to the requesting thread that the probe is in progress + # or that the current thread needs to return their findings. + key_lock = self._cache_locks[key] + key_lock.acquire() + try: + # If the by the time we get the lock the value has been + # updated we want to return the updated value. + value = self._cache_values[key] + + # In case an exception like KeyboardInterrupt is raised here. + except BaseException as e: # Defensive: + assert not isinstance(e, KeyError) # KeyError shouldn't be possible. + key_lock.release() + raise + + return value + + def set_and_release( + self, host: str, port: int, supports_http2: bool | None + ) -> None: + key = (host, port) + key_lock = self._cache_locks[key] + with key_lock: # Uses an RLock, so can be locked again from same thread. + if supports_http2 is None and self._cache_values[key] is not None: + raise ValueError( + "Cannot reset HTTP/2 support for origin after value has been set." + ) # Defensive: not expected in normal usage + + self._cache_values[key] = supports_http2 + key_lock.release() + + def _values(self) -> dict[tuple[str, int], bool | None]: + """This function is for testing purposes only. Gets the current state of the probe cache""" + with self._lock: + return {k: v for k, v in self._cache_values.items()} + + def _reset(self) -> None: + """This function is for testing purposes only. Reset the cache values""" + with self._lock: + self._cache_locks = {} + self._cache_values = {} + + +_HTTP2_PROBE_CACHE = _HTTP2ProbeCache() + +set_and_release = _HTTP2_PROBE_CACHE.set_and_release +acquire_and_get = _HTTP2_PROBE_CACHE.acquire_and_get +_values = _HTTP2_PROBE_CACHE._values +_reset = _HTTP2_PROBE_CACHE._reset + +__all__ = [ + "set_and_release", + "acquire_and_get", +] diff --git a/venv/lib/python3.13/site-packages/urllib3/util/__init__.py b/venv/lib/python3.13/site-packages/urllib3/util/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..534126033c083203649022fa9b753a433f005556 --- /dev/null +++ b/venv/lib/python3.13/site-packages/urllib3/util/__init__.py @@ -0,0 +1,42 @@ +# For backwards compatibility, provide imports that used to be here. +from __future__ import annotations + +from .connection import is_connection_dropped +from .request import SKIP_HEADER, SKIPPABLE_HEADERS, make_headers +from .response import is_fp_closed +from .retry import Retry +from .ssl_ import ( + ALPN_PROTOCOLS, + IS_PYOPENSSL, + SSLContext, + assert_fingerprint, + create_urllib3_context, + resolve_cert_reqs, + resolve_ssl_version, + ssl_wrap_socket, +) +from .timeout import Timeout +from .url import Url, parse_url +from .wait import wait_for_read, wait_for_write + +__all__ = ( + "IS_PYOPENSSL", + "SSLContext", + "ALPN_PROTOCOLS", + "Retry", + "Timeout", + "Url", + "assert_fingerprint", + "create_urllib3_context", + "is_connection_dropped", + "is_fp_closed", + "parse_url", + "make_headers", + "resolve_cert_reqs", + "resolve_ssl_version", + "ssl_wrap_socket", + "wait_for_read", + "wait_for_write", + "SKIP_HEADER", + "SKIPPABLE_HEADERS", +) diff --git a/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/__init__.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d45a13fd56bc93491a5261b17afd8820bcf7c6ea Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/__init__.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/connection.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/connection.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e21ac9c8a397b195ed04066e73384de6d929eded Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/connection.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/proxy.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/proxy.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3a8270185b26ca21ca18bad81658e82a2a002afd Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/proxy.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/request.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/request.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..96bd8de1a455cf7b8426b6dd237e5af2fec4d340 Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/request.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/response.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/response.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d84feef833ff07654126732ab1819ed76b4f9a04 Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/response.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/retry.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/retry.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1c5d0df4e5683f1925600a2ccddeed2bc68ce245 Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/retry.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/ssl_.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/ssl_.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bd3263f88320a14b6fbfdf9dbf497e0ad1965f07 Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/ssl_.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/ssl_match_hostname.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/ssl_match_hostname.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4f75a7a2336ef469de1b1e375b1d35500f1f19e1 Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/ssl_match_hostname.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/ssltransport.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/ssltransport.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c1cc22093dba09a2d80ab31b4f612c44b2981874 Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/ssltransport.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/timeout.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/timeout.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cb3d2943101b930589253da082dd609dcf708e20 Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/timeout.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/url.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/url.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a5fe89fd818c664dc9525144e4d4f51a54f2bc0a Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/url.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/util.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/util.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..472e4a27bb216c256db05cfc1bb3efbb00c5da82 Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/util.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/wait.cpython-313.pyc b/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/wait.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9b83f08ad20968fb18be24ca4d31f7c62893fce7 Binary files /dev/null and b/venv/lib/python3.13/site-packages/urllib3/util/__pycache__/wait.cpython-313.pyc differ diff --git a/venv/lib/python3.13/site-packages/urllib3/util/connection.py b/venv/lib/python3.13/site-packages/urllib3/util/connection.py new file mode 100644 index 0000000000000000000000000000000000000000..f92519ee9124e91e5da7d60ccc3f274312ed3514 --- /dev/null +++ b/venv/lib/python3.13/site-packages/urllib3/util/connection.py @@ -0,0 +1,137 @@ +from __future__ import annotations + +import socket +import typing + +from ..exceptions import LocationParseError +from .timeout import _DEFAULT_TIMEOUT, _TYPE_TIMEOUT + +_TYPE_SOCKET_OPTIONS = list[tuple[int, int, typing.Union[int, bytes]]] + +if typing.TYPE_CHECKING: + from .._base_connection import BaseHTTPConnection + + +def is_connection_dropped(conn: BaseHTTPConnection) -> bool: # Platform-specific + """ + Returns True if the connection is dropped and should be closed. + :param conn: :class:`urllib3.connection.HTTPConnection` object. + """ + return not conn.is_connected + + +# This function is copied from socket.py in the Python 2.7 standard +# library test suite. Added to its signature is only `socket_options`. +# One additional modification is that we avoid binding to IPv6 servers +# discovered in DNS if the system doesn't have IPv6 functionality. +def create_connection( + address: tuple[str, int], + timeout: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + source_address: tuple[str, int] | None = None, + socket_options: _TYPE_SOCKET_OPTIONS | None = None, +) -> socket.socket: + """Connect to *address* and return the socket object. + + Convenience function. Connect to *address* (a 2-tuple ``(host, + port)``) and return the socket object. Passing the optional + *timeout* parameter will set the timeout on the socket instance + before attempting to connect. If no *timeout* is supplied, the + global default timeout setting returned by :func:`socket.getdefaulttimeout` + is used. If *source_address* is set it must be a tuple of (host, port) + for the socket to bind as a source address before making the connection. + An host of '' or port 0 tells the OS to use the default. + """ + + host, port = address + if host.startswith("["): + host = host.strip("[]") + err = None + + # Using the value from allowed_gai_family() in the context of getaddrinfo lets + # us select whether to work with IPv4 DNS records, IPv6 records, or both. + # The original create_connection function always returns all records. + family = allowed_gai_family() + + try: + host.encode("idna") + except UnicodeError: + raise LocationParseError(f"'{host}', label empty or too long") from None + + for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM): + af, socktype, proto, canonname, sa = res + sock = None + try: + sock = socket.socket(af, socktype, proto) + + # If provided, set socket level options before connecting. + _set_socket_options(sock, socket_options) + + if timeout is not _DEFAULT_TIMEOUT: + sock.settimeout(timeout) + if source_address: + sock.bind(source_address) + sock.connect(sa) + # Break explicitly a reference cycle + err = None + return sock + + except OSError as _: + err = _ + if sock is not None: + sock.close() + + if err is not None: + try: + raise err + finally: + # Break explicitly a reference cycle + err = None + else: + raise OSError("getaddrinfo returns an empty list") + + +def _set_socket_options( + sock: socket.socket, options: _TYPE_SOCKET_OPTIONS | None +) -> None: + if options is None: + return + + for opt in options: + sock.setsockopt(*opt) + + +def allowed_gai_family() -> socket.AddressFamily: + """This function is designed to work in the context of + getaddrinfo, where family=socket.AF_UNSPEC is the default and + will perform a DNS search for both IPv6 and IPv4 records.""" + + family = socket.AF_INET + if HAS_IPV6: + family = socket.AF_UNSPEC + return family + + +def _has_ipv6(host: str) -> bool: + """Returns True if the system can bind an IPv6 address.""" + sock = None + has_ipv6 = False + + if socket.has_ipv6: + # has_ipv6 returns true if cPython was compiled with IPv6 support. + # It does not tell us if the system has IPv6 support enabled. To + # determine that we must bind to an IPv6 address. + # https://github.com/urllib3/urllib3/pull/611 + # https://bugs.python.org/issue658327 + try: + sock = socket.socket(socket.AF_INET6) + sock.bind((host, 0)) + has_ipv6 = True + except Exception: + pass + + if sock: + sock.close() + return has_ipv6 + + +HAS_IPV6 = _has_ipv6("::1") diff --git a/venv/lib/python3.13/site-packages/urllib3/util/proxy.py b/venv/lib/python3.13/site-packages/urllib3/util/proxy.py new file mode 100644 index 0000000000000000000000000000000000000000..908fc6621d0afbed16bde2c1957a5cf28d3a84d8 --- /dev/null +++ b/venv/lib/python3.13/site-packages/urllib3/util/proxy.py @@ -0,0 +1,43 @@ +from __future__ import annotations + +import typing + +from .url import Url + +if typing.TYPE_CHECKING: + from ..connection import ProxyConfig + + +def connection_requires_http_tunnel( + proxy_url: Url | None = None, + proxy_config: ProxyConfig | None = None, + destination_scheme: str | None = None, +) -> bool: + """ + Returns True if the connection requires an HTTP CONNECT through the proxy. + + :param URL proxy_url: + URL of the proxy. + :param ProxyConfig proxy_config: + Proxy configuration from poolmanager.py + :param str destination_scheme: + The scheme of the destination. (i.e https, http, etc) + """ + # If we're not using a proxy, no way to use a tunnel. + if proxy_url is None: + return False + + # HTTP destinations never require tunneling, we always forward. + if destination_scheme == "http": + return False + + # Support for forwarding with HTTPS proxies and HTTPS destinations. + if ( + proxy_url.scheme == "https" + and proxy_config + and proxy_config.use_forwarding_for_https + ): + return False + + # Otherwise always use a tunnel. + return True diff --git a/venv/lib/python3.13/site-packages/urllib3/util/request.py b/venv/lib/python3.13/site-packages/urllib3/util/request.py new file mode 100644 index 0000000000000000000000000000000000000000..23605c522b85f940f3af4db2e0561c48b0b0d269 --- /dev/null +++ b/venv/lib/python3.13/site-packages/urllib3/util/request.py @@ -0,0 +1,266 @@ +from __future__ import annotations + +import io +import typing +from base64 import b64encode +from enum import Enum + +from ..exceptions import UnrewindableBodyError +from .util import to_bytes + +if typing.TYPE_CHECKING: + from typing import Final + +# Pass as a value within ``headers`` to skip +# emitting some HTTP headers that are added automatically. +# The only headers that are supported are ``Accept-Encoding``, +# ``Host``, and ``User-Agent``. +SKIP_HEADER = "@@@SKIP_HEADER@@@" +SKIPPABLE_HEADERS = frozenset(["accept-encoding", "host", "user-agent"]) + +ACCEPT_ENCODING = "gzip,deflate" +try: + try: + import brotlicffi as _unused_module_brotli # type: ignore[import-not-found] # noqa: F401 + except ImportError: + import brotli as _unused_module_brotli # type: ignore[import-not-found] # noqa: F401 +except ImportError: + pass +else: + ACCEPT_ENCODING += ",br" + +try: + from compression import ( # type: ignore[import-not-found] # noqa: F401 + zstd as _unused_module_zstd, + ) + + ACCEPT_ENCODING += ",zstd" +except ImportError: + try: + import zstandard as _unused_module_zstd # noqa: F401 + + ACCEPT_ENCODING += ",zstd" + except ImportError: + pass + + +class _TYPE_FAILEDTELL(Enum): + token = 0 + + +_FAILEDTELL: Final[_TYPE_FAILEDTELL] = _TYPE_FAILEDTELL.token + +_TYPE_BODY_POSITION = typing.Union[int, _TYPE_FAILEDTELL] + +# When sending a request with these methods we aren't expecting +# a body so don't need to set an explicit 'Content-Length: 0' +# The reason we do this in the negative instead of tracking methods +# which 'should' have a body is because unknown methods should be +# treated as if they were 'POST' which *does* expect a body. +_METHODS_NOT_EXPECTING_BODY = {"GET", "HEAD", "DELETE", "TRACE", "OPTIONS", "CONNECT"} + + +def make_headers( + keep_alive: bool | None = None, + accept_encoding: bool | list[str] | str | None = None, + user_agent: str | None = None, + basic_auth: str | None = None, + proxy_basic_auth: str | None = None, + disable_cache: bool | None = None, +) -> dict[str, str]: + """ + Shortcuts for generating request headers. + + :param keep_alive: + If ``True``, adds 'connection: keep-alive' header. + + :param accept_encoding: + Can be a boolean, list, or string. + ``True`` translates to 'gzip,deflate'. If the dependencies for + Brotli (either the ``brotli`` or ``brotlicffi`` package) and/or Zstandard + (the ``zstandard`` package) algorithms are installed, then their encodings are + included in the string ('br' and 'zstd', respectively). + List will get joined by comma. + String will be used as provided. + + :param user_agent: + String representing the user-agent you want, such as + "python-urllib3/0.6" + + :param basic_auth: + Colon-separated username:password string for 'authorization: basic ...' + auth header. + + :param proxy_basic_auth: + Colon-separated username:password string for 'proxy-authorization: basic ...' + auth header. + + :param disable_cache: + If ``True``, adds 'cache-control: no-cache' header. + + Example: + + .. code-block:: python + + import urllib3 + + print(urllib3.util.make_headers(keep_alive=True, user_agent="Batman/1.0")) + # {'connection': 'keep-alive', 'user-agent': 'Batman/1.0'} + print(urllib3.util.make_headers(accept_encoding=True)) + # {'accept-encoding': 'gzip,deflate'} + """ + headers: dict[str, str] = {} + if accept_encoding: + if isinstance(accept_encoding, str): + pass + elif isinstance(accept_encoding, list): + accept_encoding = ",".join(accept_encoding) + else: + accept_encoding = ACCEPT_ENCODING + headers["accept-encoding"] = accept_encoding + + if user_agent: + headers["user-agent"] = user_agent + + if keep_alive: + headers["connection"] = "keep-alive" + + if basic_auth: + headers["authorization"] = ( + f"Basic {b64encode(basic_auth.encode('latin-1')).decode()}" + ) + + if proxy_basic_auth: + headers["proxy-authorization"] = ( + f"Basic {b64encode(proxy_basic_auth.encode('latin-1')).decode()}" + ) + + if disable_cache: + headers["cache-control"] = "no-cache" + + return headers + + +def set_file_position( + body: typing.Any, pos: _TYPE_BODY_POSITION | None +) -> _TYPE_BODY_POSITION | None: + """ + If a position is provided, move file to that point. + Otherwise, we'll attempt to record a position for future use. + """ + if pos is not None: + rewind_body(body, pos) + elif getattr(body, "tell", None) is not None: + try: + pos = body.tell() + except OSError: + # This differentiates from None, allowing us to catch + # a failed `tell()` later when trying to rewind the body. + pos = _FAILEDTELL + + return pos + + +def rewind_body(body: typing.IO[typing.AnyStr], body_pos: _TYPE_BODY_POSITION) -> None: + """ + Attempt to rewind body to a certain position. + Primarily used for request redirects and retries. + + :param body: + File-like object that supports seek. + + :param int pos: + Position to seek to in file. + """ + body_seek = getattr(body, "seek", None) + if body_seek is not None and isinstance(body_pos, int): + try: + body_seek(body_pos) + except OSError as e: + raise UnrewindableBodyError( + "An error occurred when rewinding request body for redirect/retry." + ) from e + elif body_pos is _FAILEDTELL: + raise UnrewindableBodyError( + "Unable to record file position for rewinding " + "request body during a redirect/retry." + ) + else: + raise ValueError( + f"body_pos must be of type integer, instead it was {type(body_pos)}." + ) + + +class ChunksAndContentLength(typing.NamedTuple): + chunks: typing.Iterable[bytes] | None + content_length: int | None + + +def body_to_chunks( + body: typing.Any | None, method: str, blocksize: int +) -> ChunksAndContentLength: + """Takes the HTTP request method, body, and blocksize and + transforms them into an iterable of chunks to pass to + socket.sendall() and an optional 'Content-Length' header. + + A 'Content-Length' of 'None' indicates the length of the body + can't be determined so should use 'Transfer-Encoding: chunked' + for framing instead. + """ + + chunks: typing.Iterable[bytes] | None + content_length: int | None + + # No body, we need to make a recommendation on 'Content-Length' + # based on whether that request method is expected to have + # a body or not. + if body is None: + chunks = None + if method.upper() not in _METHODS_NOT_EXPECTING_BODY: + content_length = 0 + else: + content_length = None + + # Bytes or strings become bytes + elif isinstance(body, (str, bytes)): + chunks = (to_bytes(body),) + content_length = len(chunks[0]) + + # File-like object, TODO: use seek() and tell() for length? + elif hasattr(body, "read"): + + def chunk_readable() -> typing.Iterable[bytes]: + nonlocal body, blocksize + encode = isinstance(body, io.TextIOBase) + while True: + datablock = body.read(blocksize) + if not datablock: + break + if encode: + datablock = datablock.encode("utf-8") + yield datablock + + chunks = chunk_readable() + content_length = None + + # Otherwise we need to start checking via duck-typing. + else: + try: + # Check if the body implements the buffer API. + mv = memoryview(body) + except TypeError: + try: + # Check if the body is an iterable + chunks = iter(body) + content_length = None + except TypeError: + raise TypeError( + f"'body' must be a bytes-like object, file-like " + f"object, or iterable. Instead was {body!r}" + ) from None + else: + # Since it implements the buffer API can be passed directly to socket.sendall() + chunks = (body,) + content_length = mv.nbytes + + return ChunksAndContentLength(chunks=chunks, content_length=content_length) diff --git a/venv/lib/python3.13/site-packages/urllib3/util/response.py b/venv/lib/python3.13/site-packages/urllib3/util/response.py new file mode 100644 index 0000000000000000000000000000000000000000..0f4578696fa2e17a900c6890ec26d65e860b0b72 --- /dev/null +++ b/venv/lib/python3.13/site-packages/urllib3/util/response.py @@ -0,0 +1,101 @@ +from __future__ import annotations + +import http.client as httplib +from email.errors import MultipartInvariantViolationDefect, StartBoundaryNotFoundDefect + +from ..exceptions import HeaderParsingError + + +def is_fp_closed(obj: object) -> bool: + """ + Checks whether a given file-like object is closed. + + :param obj: + The file-like object to check. + """ + + try: + # Check `isclosed()` first, in case Python3 doesn't set `closed`. + # GH Issue #928 + return obj.isclosed() # type: ignore[no-any-return, attr-defined] + except AttributeError: + pass + + try: + # Check via the official file-like-object way. + return obj.closed # type: ignore[no-any-return, attr-defined] + except AttributeError: + pass + + try: + # Check if the object is a container for another file-like object that + # gets released on exhaustion (e.g. HTTPResponse). + return obj.fp is None # type: ignore[attr-defined] + except AttributeError: + pass + + raise ValueError("Unable to determine whether fp is closed.") + + +def assert_header_parsing(headers: httplib.HTTPMessage) -> None: + """ + Asserts whether all headers have been successfully parsed. + Extracts encountered errors from the result of parsing headers. + + Only works on Python 3. + + :param http.client.HTTPMessage headers: Headers to verify. + + :raises urllib3.exceptions.HeaderParsingError: + If parsing errors are found. + """ + + # This will fail silently if we pass in the wrong kind of parameter. + # To make debugging easier add an explicit check. + if not isinstance(headers, httplib.HTTPMessage): + raise TypeError(f"expected httplib.Message, got {type(headers)}.") + + unparsed_data = None + + # get_payload is actually email.message.Message.get_payload; + # we're only interested in the result if it's not a multipart message + if not headers.is_multipart(): + payload = headers.get_payload() + + if isinstance(payload, (bytes, str)): + unparsed_data = payload + + # httplib is assuming a response body is available + # when parsing headers even when httplib only sends + # header data to parse_headers() This results in + # defects on multipart responses in particular. + # See: https://github.com/urllib3/urllib3/issues/800 + + # So we ignore the following defects: + # - StartBoundaryNotFoundDefect: + # The claimed start boundary was never found. + # - MultipartInvariantViolationDefect: + # A message claimed to be a multipart but no subparts were found. + defects = [ + defect + for defect in headers.defects + if not isinstance( + defect, (StartBoundaryNotFoundDefect, MultipartInvariantViolationDefect) + ) + ] + + if defects or unparsed_data: + raise HeaderParsingError(defects=defects, unparsed_data=unparsed_data) + + +def is_response_to_head(response: httplib.HTTPResponse) -> bool: + """ + Checks whether the request of a response has been a HEAD-request. + + :param http.client.HTTPResponse response: + Response to check if the originating request + used 'HEAD' as a method. + """ + # FIXME: Can we do this somehow without accessing private httplib _method? + method_str = response._method # type: str # type: ignore[attr-defined] + return method_str.upper() == "HEAD" diff --git a/venv/lib/python3.13/site-packages/urllib3/util/retry.py b/venv/lib/python3.13/site-packages/urllib3/util/retry.py new file mode 100644 index 0000000000000000000000000000000000000000..0456cceba47b16ae6784458cc17eaa528a517ffa --- /dev/null +++ b/venv/lib/python3.13/site-packages/urllib3/util/retry.py @@ -0,0 +1,533 @@ +from __future__ import annotations + +import email +import logging +import random +import re +import time +import typing +from itertools import takewhile +from types import TracebackType + +from ..exceptions import ( + ConnectTimeoutError, + InvalidHeader, + MaxRetryError, + ProtocolError, + ProxyError, + ReadTimeoutError, + ResponseError, +) +from .util import reraise + +if typing.TYPE_CHECKING: + from typing_extensions import Self + + from ..connectionpool import ConnectionPool + from ..response import BaseHTTPResponse + +log = logging.getLogger(__name__) + + +# Data structure for representing the metadata of requests that result in a retry. +class RequestHistory(typing.NamedTuple): + method: str | None + url: str | None + error: Exception | None + status: int | None + redirect_location: str | None + + +class Retry: + """Retry configuration. + + Each retry attempt will create a new Retry object with updated values, so + they can be safely reused. + + Retries can be defined as a default for a pool: + + .. code-block:: python + + retries = Retry(connect=5, read=2, redirect=5) + http = PoolManager(retries=retries) + response = http.request("GET", "https://example.com/") + + Or per-request (which overrides the default for the pool): + + .. code-block:: python + + response = http.request("GET", "https://example.com/", retries=Retry(10)) + + Retries can be disabled by passing ``False``: + + .. code-block:: python + + response = http.request("GET", "https://example.com/", retries=False) + + Errors will be wrapped in :class:`~urllib3.exceptions.MaxRetryError` unless + retries are disabled, in which case the causing exception will be raised. + + :param int total: + Total number of retries to allow. Takes precedence over other counts. + + Set to ``None`` to remove this constraint and fall back on other + counts. + + Set to ``0`` to fail on the first retry. + + Set to ``False`` to disable and imply ``raise_on_redirect=False``. + + :param int connect: + How many connection-related errors to retry on. + + These are errors raised before the request is sent to the remote server, + which we assume has not triggered the server to process the request. + + Set to ``0`` to fail on the first retry of this type. + + :param int read: + How many times to retry on read errors. + + These errors are raised after the request was sent to the server, so the + request may have side-effects. + + Set to ``0`` to fail on the first retry of this type. + + :param int redirect: + How many redirects to perform. Limit this to avoid infinite redirect + loops. + + A redirect is a HTTP response with a status code 301, 302, 303, 307 or + 308. + + Set to ``0`` to fail on the first retry of this type. + + Set to ``False`` to disable and imply ``raise_on_redirect=False``. + + :param int status: + How many times to retry on bad status codes. + + These are retries made on responses, where status code matches + ``status_forcelist``. + + Set to ``0`` to fail on the first retry of this type. + + :param int other: + How many times to retry on other errors. + + Other errors are errors that are not connect, read, redirect or status errors. + These errors might be raised after the request was sent to the server, so the + request might have side-effects. + + Set to ``0`` to fail on the first retry of this type. + + If ``total`` is not set, it's a good idea to set this to 0 to account + for unexpected edge cases and avoid infinite retry loops. + + :param Collection allowed_methods: + Set of uppercased HTTP method verbs that we should retry on. + + By default, we only retry on methods which are considered to be + idempotent (multiple requests with the same parameters end with the + same state). See :attr:`Retry.DEFAULT_ALLOWED_METHODS`. + + Set to a ``None`` value to retry on any verb. + + :param Collection status_forcelist: + A set of integer HTTP status codes that we should force a retry on. + A retry is initiated if the request method is in ``allowed_methods`` + and the response status code is in ``status_forcelist``. + + By default, this is disabled with ``None``. + + :param float backoff_factor: + A backoff factor to apply between attempts after the second try + (most errors are resolved immediately by a second try without a + delay). urllib3 will sleep for:: + + {backoff factor} * (2 ** ({number of previous retries})) + + seconds. If `backoff_jitter` is non-zero, this sleep is extended by:: + + random.uniform(0, {backoff jitter}) + + seconds. For example, if the backoff_factor is 0.1, then :func:`Retry.sleep` will + sleep for [0.0s, 0.2s, 0.4s, 0.8s, ...] between retries. No backoff will ever + be longer than `backoff_max`. + + By default, backoff is disabled (factor set to 0). + + :param bool raise_on_redirect: Whether, if the number of redirects is + exhausted, to raise a MaxRetryError, or to return a response with a + response code in the 3xx range. + + :param bool raise_on_status: Similar meaning to ``raise_on_redirect``: + whether we should raise an exception, or return a response, + if status falls in ``status_forcelist`` range and retries have + been exhausted. + + :param tuple history: The history of the request encountered during + each call to :meth:`~Retry.increment`. The list is in the order + the requests occurred. Each list item is of class :class:`RequestHistory`. + + :param bool respect_retry_after_header: + Whether to respect Retry-After header on status codes defined as + :attr:`Retry.RETRY_AFTER_STATUS_CODES` or not. + + :param Collection remove_headers_on_redirect: + Sequence of headers to remove from the request when a response + indicating a redirect is returned before firing off the redirected + request. + """ + + #: Default methods to be used for ``allowed_methods`` + DEFAULT_ALLOWED_METHODS = frozenset( + ["HEAD", "GET", "PUT", "DELETE", "OPTIONS", "TRACE"] + ) + + #: Default status codes to be used for ``status_forcelist`` + RETRY_AFTER_STATUS_CODES = frozenset([413, 429, 503]) + + #: Default headers to be used for ``remove_headers_on_redirect`` + DEFAULT_REMOVE_HEADERS_ON_REDIRECT = frozenset( + ["Cookie", "Authorization", "Proxy-Authorization"] + ) + + #: Default maximum backoff time. + DEFAULT_BACKOFF_MAX = 120 + + # Backward compatibility; assigned outside of the class. + DEFAULT: typing.ClassVar[Retry] + + def __init__( + self, + total: bool | int | None = 10, + connect: int | None = None, + read: int | None = None, + redirect: bool | int | None = None, + status: int | None = None, + other: int | None = None, + allowed_methods: typing.Collection[str] | None = DEFAULT_ALLOWED_METHODS, + status_forcelist: typing.Collection[int] | None = None, + backoff_factor: float = 0, + backoff_max: float = DEFAULT_BACKOFF_MAX, + raise_on_redirect: bool = True, + raise_on_status: bool = True, + history: tuple[RequestHistory, ...] | None = None, + respect_retry_after_header: bool = True, + remove_headers_on_redirect: typing.Collection[ + str + ] = DEFAULT_REMOVE_HEADERS_ON_REDIRECT, + backoff_jitter: float = 0.0, + ) -> None: + self.total = total + self.connect = connect + self.read = read + self.status = status + self.other = other + + if redirect is False or total is False: + redirect = 0 + raise_on_redirect = False + + self.redirect = redirect + self.status_forcelist = status_forcelist or set() + self.allowed_methods = allowed_methods + self.backoff_factor = backoff_factor + self.backoff_max = backoff_max + self.raise_on_redirect = raise_on_redirect + self.raise_on_status = raise_on_status + self.history = history or () + self.respect_retry_after_header = respect_retry_after_header + self.remove_headers_on_redirect = frozenset( + h.lower() for h in remove_headers_on_redirect + ) + self.backoff_jitter = backoff_jitter + + def new(self, **kw: typing.Any) -> Self: + params = dict( + total=self.total, + connect=self.connect, + read=self.read, + redirect=self.redirect, + status=self.status, + other=self.other, + allowed_methods=self.allowed_methods, + status_forcelist=self.status_forcelist, + backoff_factor=self.backoff_factor, + backoff_max=self.backoff_max, + raise_on_redirect=self.raise_on_redirect, + raise_on_status=self.raise_on_status, + history=self.history, + remove_headers_on_redirect=self.remove_headers_on_redirect, + respect_retry_after_header=self.respect_retry_after_header, + backoff_jitter=self.backoff_jitter, + ) + + params.update(kw) + return type(self)(**params) # type: ignore[arg-type] + + @classmethod + def from_int( + cls, + retries: Retry | bool | int | None, + redirect: bool | int | None = True, + default: Retry | bool | int | None = None, + ) -> Retry: + """Backwards-compatibility for the old retries format.""" + if retries is None: + retries = default if default is not None else cls.DEFAULT + + if isinstance(retries, Retry): + return retries + + redirect = bool(redirect) and None + new_retries = cls(retries, redirect=redirect) + log.debug("Converted retries value: %r -> %r", retries, new_retries) + return new_retries + + def get_backoff_time(self) -> float: + """Formula for computing the current backoff + + :rtype: float + """ + # We want to consider only the last consecutive errors sequence (Ignore redirects). + consecutive_errors_len = len( + list( + takewhile(lambda x: x.redirect_location is None, reversed(self.history)) + ) + ) + if consecutive_errors_len <= 1: + return 0 + + backoff_value = self.backoff_factor * (2 ** (consecutive_errors_len - 1)) + if self.backoff_jitter != 0.0: + backoff_value += random.random() * self.backoff_jitter + return float(max(0, min(self.backoff_max, backoff_value))) + + def parse_retry_after(self, retry_after: str) -> float: + seconds: float + # Whitespace: https://tools.ietf.org/html/rfc7230#section-3.2.4 + if re.match(r"^\s*[0-9]+\s*$", retry_after): + seconds = int(retry_after) + else: + retry_date_tuple = email.utils.parsedate_tz(retry_after) + if retry_date_tuple is None: + raise InvalidHeader(f"Invalid Retry-After header: {retry_after}") + + retry_date = email.utils.mktime_tz(retry_date_tuple) + seconds = retry_date - time.time() + + seconds = max(seconds, 0) + + return seconds + + def get_retry_after(self, response: BaseHTTPResponse) -> float | None: + """Get the value of Retry-After in seconds.""" + + retry_after = response.headers.get("Retry-After") + + if retry_after is None: + return None + + return self.parse_retry_after(retry_after) + + def sleep_for_retry(self, response: BaseHTTPResponse) -> bool: + retry_after = self.get_retry_after(response) + if retry_after: + time.sleep(retry_after) + return True + + return False + + def _sleep_backoff(self) -> None: + backoff = self.get_backoff_time() + if backoff <= 0: + return + time.sleep(backoff) + + def sleep(self, response: BaseHTTPResponse | None = None) -> None: + """Sleep between retry attempts. + + This method will respect a server's ``Retry-After`` response header + and sleep the duration of the time requested. If that is not present, it + will use an exponential backoff. By default, the backoff factor is 0 and + this method will return immediately. + """ + + if self.respect_retry_after_header and response: + slept = self.sleep_for_retry(response) + if slept: + return + + self._sleep_backoff() + + def _is_connection_error(self, err: Exception) -> bool: + """Errors when we're fairly sure that the server did not receive the + request, so it should be safe to retry. + """ + if isinstance(err, ProxyError): + err = err.original_error + return isinstance(err, ConnectTimeoutError) + + def _is_read_error(self, err: Exception) -> bool: + """Errors that occur after the request has been started, so we should + assume that the server began processing it. + """ + return isinstance(err, (ReadTimeoutError, ProtocolError)) + + def _is_method_retryable(self, method: str) -> bool: + """Checks if a given HTTP method should be retried upon, depending if + it is included in the allowed_methods + """ + if self.allowed_methods and method.upper() not in self.allowed_methods: + return False + return True + + def is_retry( + self, method: str, status_code: int, has_retry_after: bool = False + ) -> bool: + """Is this method/status code retryable? (Based on allowlists and control + variables such as the number of total retries to allow, whether to + respect the Retry-After header, whether this header is present, and + whether the returned status code is on the list of status codes to + be retried upon on the presence of the aforementioned header) + """ + if not self._is_method_retryable(method): + return False + + if self.status_forcelist and status_code in self.status_forcelist: + return True + + return bool( + self.total + and self.respect_retry_after_header + and has_retry_after + and (status_code in self.RETRY_AFTER_STATUS_CODES) + ) + + def is_exhausted(self) -> bool: + """Are we out of retries?""" + retry_counts = [ + x + for x in ( + self.total, + self.connect, + self.read, + self.redirect, + self.status, + self.other, + ) + if x + ] + if not retry_counts: + return False + + return min(retry_counts) < 0 + + def increment( + self, + method: str | None = None, + url: str | None = None, + response: BaseHTTPResponse | None = None, + error: Exception | None = None, + _pool: ConnectionPool | None = None, + _stacktrace: TracebackType | None = None, + ) -> Self: + """Return a new Retry object with incremented retry counters. + + :param response: A response object, or None, if the server did not + return a response. + :type response: :class:`~urllib3.response.BaseHTTPResponse` + :param Exception error: An error encountered during the request, or + None if the response was received successfully. + + :return: A new ``Retry`` object. + """ + if self.total is False and error: + # Disabled, indicate to re-raise the error. + raise reraise(type(error), error, _stacktrace) + + total = self.total + if total is not None: + total -= 1 + + connect = self.connect + read = self.read + redirect = self.redirect + status_count = self.status + other = self.other + cause = "unknown" + status = None + redirect_location = None + + if error and self._is_connection_error(error): + # Connect retry? + if connect is False: + raise reraise(type(error), error, _stacktrace) + elif connect is not None: + connect -= 1 + + elif error and self._is_read_error(error): + # Read retry? + if read is False or method is None or not self._is_method_retryable(method): + raise reraise(type(error), error, _stacktrace) + elif read is not None: + read -= 1 + + elif error: + # Other retry? + if other is not None: + other -= 1 + + elif response and response.get_redirect_location(): + # Redirect retry? + if redirect is not None: + redirect -= 1 + cause = "too many redirects" + response_redirect_location = response.get_redirect_location() + if response_redirect_location: + redirect_location = response_redirect_location + status = response.status + + else: + # Incrementing because of a server error like a 500 in + # status_forcelist and the given method is in the allowed_methods + cause = ResponseError.GENERIC_ERROR + if response and response.status: + if status_count is not None: + status_count -= 1 + cause = ResponseError.SPECIFIC_ERROR.format(status_code=response.status) + status = response.status + + history = self.history + ( + RequestHistory(method, url, error, status, redirect_location), + ) + + new_retry = self.new( + total=total, + connect=connect, + read=read, + redirect=redirect, + status=status_count, + other=other, + history=history, + ) + + if new_retry.is_exhausted(): + reason = error or ResponseError(cause) + raise MaxRetryError(_pool, url, reason) from reason # type: ignore[arg-type] + + log.debug("Incremented Retry for (url='%s'): %r", url, new_retry) + + return new_retry + + def __repr__(self) -> str: + return ( + f"{type(self).__name__}(total={self.total}, connect={self.connect}, " + f"read={self.read}, redirect={self.redirect}, status={self.status})" + ) + + +# For backwards compatibility (equivalent to pre-v1.9): +Retry.DEFAULT = Retry(3) diff --git a/venv/lib/python3.13/site-packages/urllib3/util/ssl_.py b/venv/lib/python3.13/site-packages/urllib3/util/ssl_.py new file mode 100644 index 0000000000000000000000000000000000000000..b2cc1aa7bb5bb934099bfb44c935b8eccc73f4e0 --- /dev/null +++ b/venv/lib/python3.13/site-packages/urllib3/util/ssl_.py @@ -0,0 +1,524 @@ +from __future__ import annotations + +import hashlib +import hmac +import os +import socket +import sys +import typing +import warnings +from binascii import unhexlify + +from ..exceptions import ProxySchemeUnsupported, SSLError +from .url import _BRACELESS_IPV6_ADDRZ_RE, _IPV4_RE + +SSLContext = None +SSLTransport = None +HAS_NEVER_CHECK_COMMON_NAME = False +IS_PYOPENSSL = False +ALPN_PROTOCOLS = ["http/1.1"] + +_TYPE_VERSION_INFO = tuple[int, int, int, str, int] + +# Maps the length of a digest to a possible hash function producing this digest +HASHFUNC_MAP = { + length: getattr(hashlib, algorithm, None) + for length, algorithm in ((32, "md5"), (40, "sha1"), (64, "sha256")) +} + + +def _is_bpo_43522_fixed( + implementation_name: str, + version_info: _TYPE_VERSION_INFO, + pypy_version_info: _TYPE_VERSION_INFO | None, +) -> bool: + """Return True for CPython 3.9.3+ or 3.10+ and PyPy 7.3.8+ where + setting SSLContext.hostname_checks_common_name to False works. + + Outside of CPython and PyPy we don't know which implementations work + or not so we conservatively use our hostname matching as we know that works + on all implementations. + + https://github.com/urllib3/urllib3/issues/2192#issuecomment-821832963 + https://foss.heptapod.net/pypy/pypy/-/issues/3539 + """ + if implementation_name == "pypy": + # https://foss.heptapod.net/pypy/pypy/-/issues/3129 + return pypy_version_info >= (7, 3, 8) # type: ignore[operator] + elif implementation_name == "cpython": + major_minor = version_info[:2] + micro = version_info[2] + return (major_minor == (3, 9) and micro >= 3) or major_minor >= (3, 10) + else: # Defensive: + return False + + +def _is_has_never_check_common_name_reliable( + openssl_version: str, + openssl_version_number: int, + implementation_name: str, + version_info: _TYPE_VERSION_INFO, + pypy_version_info: _TYPE_VERSION_INFO | None, +) -> bool: + # As of May 2023, all released versions of LibreSSL fail to reject certificates with + # only common names, see https://github.com/urllib3/urllib3/pull/3024 + is_openssl = openssl_version.startswith("OpenSSL ") + # Before fixing OpenSSL issue #14579, the SSL_new() API was not copying hostflags + # like X509_CHECK_FLAG_NEVER_CHECK_SUBJECT, which tripped up CPython. + # https://github.com/openssl/openssl/issues/14579 + # This was released in OpenSSL 1.1.1l+ (>=0x101010cf) + is_openssl_issue_14579_fixed = openssl_version_number >= 0x101010CF + + return is_openssl and ( + is_openssl_issue_14579_fixed + or _is_bpo_43522_fixed(implementation_name, version_info, pypy_version_info) + ) + + +if typing.TYPE_CHECKING: + from ssl import VerifyMode + from typing import TypedDict + + from .ssltransport import SSLTransport as SSLTransportType + + class _TYPE_PEER_CERT_RET_DICT(TypedDict, total=False): + subjectAltName: tuple[tuple[str, str], ...] + subject: tuple[tuple[tuple[str, str], ...], ...] + serialNumber: str + + +# Mapping from 'ssl.PROTOCOL_TLSX' to 'TLSVersion.X' +_SSL_VERSION_TO_TLS_VERSION: dict[int, int] = {} + +try: # Do we have ssl at all? + import ssl + from ssl import ( # type: ignore[assignment] + CERT_REQUIRED, + HAS_NEVER_CHECK_COMMON_NAME, + OP_NO_COMPRESSION, + OP_NO_TICKET, + OPENSSL_VERSION, + OPENSSL_VERSION_NUMBER, + PROTOCOL_TLS, + PROTOCOL_TLS_CLIENT, + VERIFY_X509_STRICT, + OP_NO_SSLv2, + OP_NO_SSLv3, + SSLContext, + TLSVersion, + ) + + PROTOCOL_SSLv23 = PROTOCOL_TLS + + # Needed for Python 3.9 which does not define this + VERIFY_X509_PARTIAL_CHAIN = getattr(ssl, "VERIFY_X509_PARTIAL_CHAIN", 0x80000) + + # Setting SSLContext.hostname_checks_common_name = False didn't work before CPython + # 3.9.3, and 3.10 (but OK on PyPy) or OpenSSL 1.1.1l+ + if HAS_NEVER_CHECK_COMMON_NAME and not _is_has_never_check_common_name_reliable( + OPENSSL_VERSION, + OPENSSL_VERSION_NUMBER, + sys.implementation.name, + sys.version_info, + sys.pypy_version_info if sys.implementation.name == "pypy" else None, # type: ignore[attr-defined] + ): # Defensive: for Python < 3.9.3 + HAS_NEVER_CHECK_COMMON_NAME = False + + # Need to be careful here in case old TLS versions get + # removed in future 'ssl' module implementations. + for attr in ("TLSv1", "TLSv1_1", "TLSv1_2"): + try: + _SSL_VERSION_TO_TLS_VERSION[getattr(ssl, f"PROTOCOL_{attr}")] = getattr( + TLSVersion, attr + ) + except AttributeError: # Defensive: + continue + + from .ssltransport import SSLTransport # type: ignore[assignment] +except ImportError: + OP_NO_COMPRESSION = 0x20000 # type: ignore[assignment] + OP_NO_TICKET = 0x4000 # type: ignore[assignment] + OP_NO_SSLv2 = 0x1000000 # type: ignore[assignment] + OP_NO_SSLv3 = 0x2000000 # type: ignore[assignment] + PROTOCOL_SSLv23 = PROTOCOL_TLS = 2 # type: ignore[assignment] + PROTOCOL_TLS_CLIENT = 16 # type: ignore[assignment] + VERIFY_X509_PARTIAL_CHAIN = 0x80000 + VERIFY_X509_STRICT = 0x20 # type: ignore[assignment] + + +_TYPE_PEER_CERT_RET = typing.Union["_TYPE_PEER_CERT_RET_DICT", bytes, None] + + +def assert_fingerprint(cert: bytes | None, fingerprint: str) -> None: + """ + Checks if given fingerprint matches the supplied certificate. + + :param cert: + Certificate as bytes object. + :param fingerprint: + Fingerprint as string of hexdigits, can be interspersed by colons. + """ + + if cert is None: + raise SSLError("No certificate for the peer.") + + fingerprint = fingerprint.replace(":", "").lower() + digest_length = len(fingerprint) + if digest_length not in HASHFUNC_MAP: + raise SSLError(f"Fingerprint of invalid length: {fingerprint}") + hashfunc = HASHFUNC_MAP.get(digest_length) + if hashfunc is None: + raise SSLError( + f"Hash function implementation unavailable for fingerprint length: {digest_length}" + ) + + # We need encode() here for py32; works on py2 and p33. + fingerprint_bytes = unhexlify(fingerprint.encode()) + + cert_digest = hashfunc(cert).digest() + + if not hmac.compare_digest(cert_digest, fingerprint_bytes): + raise SSLError( + f'Fingerprints did not match. Expected "{fingerprint}", got "{cert_digest.hex()}"' + ) + + +def resolve_cert_reqs(candidate: None | int | str) -> VerifyMode: + """ + Resolves the argument to a numeric constant, which can be passed to + the wrap_socket function/method from the ssl module. + Defaults to :data:`ssl.CERT_REQUIRED`. + If given a string it is assumed to be the name of the constant in the + :mod:`ssl` module or its abbreviation. + (So you can specify `REQUIRED` instead of `CERT_REQUIRED`. + If it's neither `None` nor a string we assume it is already the numeric + constant which can directly be passed to wrap_socket. + """ + if candidate is None: + return CERT_REQUIRED + + if isinstance(candidate, str): + res = getattr(ssl, candidate, None) + if res is None: + res = getattr(ssl, "CERT_" + candidate) + return res # type: ignore[no-any-return] + + return candidate # type: ignore[return-value] + + +def resolve_ssl_version(candidate: None | int | str) -> int: + """ + like resolve_cert_reqs + """ + if candidate is None: + return PROTOCOL_TLS + + if isinstance(candidate, str): + res = getattr(ssl, candidate, None) + if res is None: + res = getattr(ssl, "PROTOCOL_" + candidate) + return typing.cast(int, res) + + return candidate + + +def create_urllib3_context( + ssl_version: int | None = None, + cert_reqs: int | None = None, + options: int | None = None, + ciphers: str | None = None, + ssl_minimum_version: int | None = None, + ssl_maximum_version: int | None = None, + verify_flags: int | None = None, +) -> ssl.SSLContext: + """Creates and configures an :class:`ssl.SSLContext` instance for use with urllib3. + + :param ssl_version: + The desired protocol version to use. This will default to + PROTOCOL_SSLv23 which will negotiate the highest protocol that both + the server and your installation of OpenSSL support. + + This parameter is deprecated instead use 'ssl_minimum_version'. + :param ssl_minimum_version: + The minimum version of TLS to be used. Use the 'ssl.TLSVersion' enum for specifying the value. + :param ssl_maximum_version: + The maximum version of TLS to be used. Use the 'ssl.TLSVersion' enum for specifying the value. + Not recommended to set to anything other than 'ssl.TLSVersion.MAXIMUM_SUPPORTED' which is the + default value. + :param cert_reqs: + Whether to require the certificate verification. This defaults to + ``ssl.CERT_REQUIRED``. + :param options: + Specific OpenSSL options. These default to ``ssl.OP_NO_SSLv2``, + ``ssl.OP_NO_SSLv3``, ``ssl.OP_NO_COMPRESSION``, and ``ssl.OP_NO_TICKET``. + :param ciphers: + Which cipher suites to allow the server to select. Defaults to either system configured + ciphers if OpenSSL 1.1.1+, otherwise uses a secure default set of ciphers. + :param verify_flags: + The flags for certificate verification operations. These default to + ``ssl.VERIFY_X509_PARTIAL_CHAIN`` and ``ssl.VERIFY_X509_STRICT`` for Python 3.13+. + :returns: + Constructed SSLContext object with specified options + :rtype: SSLContext + """ + if SSLContext is None: + raise TypeError("Can't create an SSLContext object without an ssl module") + + # This means 'ssl_version' was specified as an exact value. + if ssl_version not in (None, PROTOCOL_TLS, PROTOCOL_TLS_CLIENT): + # Disallow setting 'ssl_version' and 'ssl_minimum|maximum_version' + # to avoid conflicts. + if ssl_minimum_version is not None or ssl_maximum_version is not None: + raise ValueError( + "Can't specify both 'ssl_version' and either " + "'ssl_minimum_version' or 'ssl_maximum_version'" + ) + + # 'ssl_version' is deprecated and will be removed in the future. + else: + # Use 'ssl_minimum_version' and 'ssl_maximum_version' instead. + ssl_minimum_version = _SSL_VERSION_TO_TLS_VERSION.get( + ssl_version, TLSVersion.MINIMUM_SUPPORTED + ) + ssl_maximum_version = _SSL_VERSION_TO_TLS_VERSION.get( + ssl_version, TLSVersion.MAXIMUM_SUPPORTED + ) + + # This warning message is pushing users to use 'ssl_minimum_version' + # instead of both min/max. Best practice is to only set the minimum version and + # keep the maximum version to be it's default value: 'TLSVersion.MAXIMUM_SUPPORTED' + warnings.warn( + "'ssl_version' option is deprecated and will be " + "removed in urllib3 v2.6.0. Instead use 'ssl_minimum_version'", + category=DeprecationWarning, + stacklevel=2, + ) + + # PROTOCOL_TLS is deprecated in Python 3.10 so we always use PROTOCOL_TLS_CLIENT + context = SSLContext(PROTOCOL_TLS_CLIENT) + + if ssl_minimum_version is not None: + context.minimum_version = ssl_minimum_version + else: # Python <3.10 defaults to 'MINIMUM_SUPPORTED' so explicitly set TLSv1.2 here + context.minimum_version = TLSVersion.TLSv1_2 + + if ssl_maximum_version is not None: + context.maximum_version = ssl_maximum_version + + # Unless we're given ciphers defer to either system ciphers in + # the case of OpenSSL 1.1.1+ or use our own secure default ciphers. + if ciphers: + context.set_ciphers(ciphers) + + # Setting the default here, as we may have no ssl module on import + cert_reqs = ssl.CERT_REQUIRED if cert_reqs is None else cert_reqs + + if options is None: + options = 0 + # SSLv2 is easily broken and is considered harmful and dangerous + options |= OP_NO_SSLv2 + # SSLv3 has several problems and is now dangerous + options |= OP_NO_SSLv3 + # Disable compression to prevent CRIME attacks for OpenSSL 1.0+ + # (issue #309) + options |= OP_NO_COMPRESSION + # TLSv1.2 only. Unless set explicitly, do not request tickets. + # This may save some bandwidth on wire, and although the ticket is encrypted, + # there is a risk associated with it being on wire, + # if the server is not rotating its ticketing keys properly. + options |= OP_NO_TICKET + + context.options |= options + + if verify_flags is None: + verify_flags = 0 + # In Python 3.13+ ssl.create_default_context() sets VERIFY_X509_PARTIAL_CHAIN + # and VERIFY_X509_STRICT so we do the same + if sys.version_info >= (3, 13): + verify_flags |= VERIFY_X509_PARTIAL_CHAIN + verify_flags |= VERIFY_X509_STRICT + + context.verify_flags |= verify_flags + + # Enable post-handshake authentication for TLS 1.3, see GH #1634. PHA is + # necessary for conditional client cert authentication with TLS 1.3. + # The attribute is None for OpenSSL <= 1.1.0 or does not exist when using + # an SSLContext created by pyOpenSSL. + if getattr(context, "post_handshake_auth", None) is not None: + context.post_handshake_auth = True + + # The order of the below lines setting verify_mode and check_hostname + # matter due to safe-guards SSLContext has to prevent an SSLContext with + # check_hostname=True, verify_mode=NONE/OPTIONAL. + # We always set 'check_hostname=False' for pyOpenSSL so we rely on our own + # 'ssl.match_hostname()' implementation. + if cert_reqs == ssl.CERT_REQUIRED and not IS_PYOPENSSL: + context.verify_mode = cert_reqs + context.check_hostname = True + else: + context.check_hostname = False + context.verify_mode = cert_reqs + + try: + context.hostname_checks_common_name = False + except AttributeError: # Defensive: for CPython < 3.9.3; for PyPy < 7.3.8 + pass + + sslkeylogfile = os.environ.get("SSLKEYLOGFILE") + if sslkeylogfile: + context.keylog_filename = sslkeylogfile + + return context + + +@typing.overload +def ssl_wrap_socket( + sock: socket.socket, + keyfile: str | None = ..., + certfile: str | None = ..., + cert_reqs: int | None = ..., + ca_certs: str | None = ..., + server_hostname: str | None = ..., + ssl_version: int | None = ..., + ciphers: str | None = ..., + ssl_context: ssl.SSLContext | None = ..., + ca_cert_dir: str | None = ..., + key_password: str | None = ..., + ca_cert_data: None | str | bytes = ..., + tls_in_tls: typing.Literal[False] = ..., +) -> ssl.SSLSocket: ... + + +@typing.overload +def ssl_wrap_socket( + sock: socket.socket, + keyfile: str | None = ..., + certfile: str | None = ..., + cert_reqs: int | None = ..., + ca_certs: str | None = ..., + server_hostname: str | None = ..., + ssl_version: int | None = ..., + ciphers: str | None = ..., + ssl_context: ssl.SSLContext | None = ..., + ca_cert_dir: str | None = ..., + key_password: str | None = ..., + ca_cert_data: None | str | bytes = ..., + tls_in_tls: bool = ..., +) -> ssl.SSLSocket | SSLTransportType: ... + + +def ssl_wrap_socket( + sock: socket.socket, + keyfile: str | None = None, + certfile: str | None = None, + cert_reqs: int | None = None, + ca_certs: str | None = None, + server_hostname: str | None = None, + ssl_version: int | None = None, + ciphers: str | None = None, + ssl_context: ssl.SSLContext | None = None, + ca_cert_dir: str | None = None, + key_password: str | None = None, + ca_cert_data: None | str | bytes = None, + tls_in_tls: bool = False, +) -> ssl.SSLSocket | SSLTransportType: + """ + All arguments except for server_hostname, ssl_context, tls_in_tls, ca_cert_data and + ca_cert_dir have the same meaning as they do when using + :func:`ssl.create_default_context`, :meth:`ssl.SSLContext.load_cert_chain`, + :meth:`ssl.SSLContext.set_ciphers` and :meth:`ssl.SSLContext.wrap_socket`. + + :param server_hostname: + When SNI is supported, the expected hostname of the certificate + :param ssl_context: + A pre-made :class:`SSLContext` object. If none is provided, one will + be created using :func:`create_urllib3_context`. + :param ciphers: + A string of ciphers we wish the client to support. + :param ca_cert_dir: + A directory containing CA certificates in multiple separate files, as + supported by OpenSSL's -CApath flag or the capath argument to + SSLContext.load_verify_locations(). + :param key_password: + Optional password if the keyfile is encrypted. + :param ca_cert_data: + Optional string containing CA certificates in PEM format suitable for + passing as the cadata parameter to SSLContext.load_verify_locations() + :param tls_in_tls: + Use SSLTransport to wrap the existing socket. + """ + context = ssl_context + if context is None: + # Note: This branch of code and all the variables in it are only used in tests. + # We should consider deprecating and removing this code. + context = create_urllib3_context(ssl_version, cert_reqs, ciphers=ciphers) + + if ca_certs or ca_cert_dir or ca_cert_data: + try: + context.load_verify_locations(ca_certs, ca_cert_dir, ca_cert_data) + except OSError as e: + raise SSLError(e) from e + + elif ssl_context is None and hasattr(context, "load_default_certs"): + # try to load OS default certs; works well on Windows. + context.load_default_certs() + + # Attempt to detect if we get the goofy behavior of the + # keyfile being encrypted and OpenSSL asking for the + # passphrase via the terminal and instead error out. + if keyfile and key_password is None and _is_key_file_encrypted(keyfile): + raise SSLError("Client private key is encrypted, password is required") + + if certfile: + if key_password is None: + context.load_cert_chain(certfile, keyfile) + else: + context.load_cert_chain(certfile, keyfile, key_password) + + context.set_alpn_protocols(ALPN_PROTOCOLS) + + ssl_sock = _ssl_wrap_socket_impl(sock, context, tls_in_tls, server_hostname) + return ssl_sock + + +def is_ipaddress(hostname: str | bytes) -> bool: + """Detects whether the hostname given is an IPv4 or IPv6 address. + Also detects IPv6 addresses with Zone IDs. + + :param str hostname: Hostname to examine. + :return: True if the hostname is an IP address, False otherwise. + """ + if isinstance(hostname, bytes): + # IDN A-label bytes are ASCII compatible. + hostname = hostname.decode("ascii") + return bool(_IPV4_RE.match(hostname) or _BRACELESS_IPV6_ADDRZ_RE.match(hostname)) + + +def _is_key_file_encrypted(key_file: str) -> bool: + """Detects if a key file is encrypted or not.""" + with open(key_file) as f: + for line in f: + # Look for Proc-Type: 4,ENCRYPTED + if "ENCRYPTED" in line: + return True + + return False + + +def _ssl_wrap_socket_impl( + sock: socket.socket, + ssl_context: ssl.SSLContext, + tls_in_tls: bool, + server_hostname: str | None = None, +) -> ssl.SSLSocket | SSLTransportType: + if tls_in_tls: + if not SSLTransport: + # Import error, ssl is not available. + raise ProxySchemeUnsupported( + "TLS in TLS requires support for the 'ssl' module" + ) + + SSLTransport._validate_ssl_context_for_tls_in_tls(ssl_context) + return SSLTransport(sock, ssl_context, server_hostname) + + return ssl_context.wrap_socket(sock, server_hostname=server_hostname) diff --git a/venv/lib/python3.13/site-packages/urllib3/util/ssl_match_hostname.py b/venv/lib/python3.13/site-packages/urllib3/util/ssl_match_hostname.py new file mode 100644 index 0000000000000000000000000000000000000000..25d91000419ea4a860f511ebe669fe171b79254c --- /dev/null +++ b/venv/lib/python3.13/site-packages/urllib3/util/ssl_match_hostname.py @@ -0,0 +1,159 @@ +"""The match_hostname() function from Python 3.5, essential when using SSL.""" + +# Note: This file is under the PSF license as the code comes from the python +# stdlib. http://docs.python.org/3/license.html +# It is modified to remove commonName support. + +from __future__ import annotations + +import ipaddress +import re +import typing +from ipaddress import IPv4Address, IPv6Address + +if typing.TYPE_CHECKING: + from .ssl_ import _TYPE_PEER_CERT_RET_DICT + +__version__ = "3.5.0.1" + + +class CertificateError(ValueError): + pass + + +def _dnsname_match( + dn: typing.Any, hostname: str, max_wildcards: int = 1 +) -> typing.Match[str] | None | bool: + """Matching according to RFC 6125, section 6.4.3 + + http://tools.ietf.org/html/rfc6125#section-6.4.3 + """ + pats = [] + if not dn: + return False + + # Ported from python3-syntax: + # leftmost, *remainder = dn.split(r'.') + parts = dn.split(r".") + leftmost = parts[0] + remainder = parts[1:] + + wildcards = leftmost.count("*") + if wildcards > max_wildcards: + # Issue #17980: avoid denials of service by refusing more + # than one wildcard per fragment. A survey of established + # policy among SSL implementations showed it to be a + # reasonable choice. + raise CertificateError( + "too many wildcards in certificate DNS name: " + repr(dn) + ) + + # speed up common case w/o wildcards + if not wildcards: + return bool(dn.lower() == hostname.lower()) + + # RFC 6125, section 6.4.3, subitem 1. + # The client SHOULD NOT attempt to match a presented identifier in which + # the wildcard character comprises a label other than the left-most label. + if leftmost == "*": + # When '*' is a fragment by itself, it matches a non-empty dotless + # fragment. + pats.append("[^.]+") + elif leftmost.startswith("xn--") or hostname.startswith("xn--"): + # RFC 6125, section 6.4.3, subitem 3. + # The client SHOULD NOT attempt to match a presented identifier + # where the wildcard character is embedded within an A-label or + # U-label of an internationalized domain name. + pats.append(re.escape(leftmost)) + else: + # Otherwise, '*' matches any dotless string, e.g. www* + pats.append(re.escape(leftmost).replace(r"\*", "[^.]*")) + + # add the remaining fragments, ignore any wildcards + for frag in remainder: + pats.append(re.escape(frag)) + + pat = re.compile(r"\A" + r"\.".join(pats) + r"\Z", re.IGNORECASE) + return pat.match(hostname) + + +def _ipaddress_match(ipname: str, host_ip: IPv4Address | IPv6Address) -> bool: + """Exact matching of IP addresses. + + RFC 9110 section 4.3.5: "A reference identity of IP-ID contains the decoded + bytes of the IP address. An IP version 4 address is 4 octets, and an IP + version 6 address is 16 octets. [...] A reference identity of type IP-ID + matches if the address is identical to an iPAddress value of the + subjectAltName extension of the certificate." + """ + # OpenSSL may add a trailing newline to a subjectAltName's IP address + # Divergence from upstream: ipaddress can't handle byte str + ip = ipaddress.ip_address(ipname.rstrip()) + return bool(ip.packed == host_ip.packed) + + +def match_hostname( + cert: _TYPE_PEER_CERT_RET_DICT | None, + hostname: str, + hostname_checks_common_name: bool = False, +) -> None: + """Verify that *cert* (in decoded format as returned by + SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125 + rules are followed, but IP addresses are not accepted for *hostname*. + + CertificateError is raised on failure. On success, the function + returns nothing. + """ + if not cert: + raise ValueError( + "empty or no certificate, match_hostname needs a " + "SSL socket or SSL context with either " + "CERT_OPTIONAL or CERT_REQUIRED" + ) + try: + # Divergence from upstream: ipaddress can't handle byte str + # + # The ipaddress module shipped with Python < 3.9 does not support + # scoped IPv6 addresses so we unconditionally strip the Zone IDs for + # now. Once we drop support for Python 3.9 we can remove this branch. + if "%" in hostname: + host_ip = ipaddress.ip_address(hostname[: hostname.rfind("%")]) + else: + host_ip = ipaddress.ip_address(hostname) + + except ValueError: + # Not an IP address (common case) + host_ip = None + dnsnames = [] + san: tuple[tuple[str, str], ...] = cert.get("subjectAltName", ()) + key: str + value: str + for key, value in san: + if key == "DNS": + if host_ip is None and _dnsname_match(value, hostname): + return + dnsnames.append(value) + elif key == "IP Address": + if host_ip is not None and _ipaddress_match(value, host_ip): + return + dnsnames.append(value) + + # We only check 'commonName' if it's enabled and we're not verifying + # an IP address. IP addresses aren't valid within 'commonName'. + if hostname_checks_common_name and host_ip is None and not dnsnames: + for sub in cert.get("subject", ()): + for key, value in sub: + if key == "commonName": + if _dnsname_match(value, hostname): + return + dnsnames.append(value) # Defensive: for Python < 3.9.3 + + if len(dnsnames) > 1: + raise CertificateError( + "hostname %r " + "doesn't match either of %s" % (hostname, ", ".join(map(repr, dnsnames))) + ) + elif len(dnsnames) == 1: + raise CertificateError(f"hostname {hostname!r} doesn't match {dnsnames[0]!r}") + else: + raise CertificateError("no appropriate subjectAltName fields were found") diff --git a/venv/lib/python3.13/site-packages/urllib3/util/ssltransport.py b/venv/lib/python3.13/site-packages/urllib3/util/ssltransport.py new file mode 100644 index 0000000000000000000000000000000000000000..6d59bc3bce2489c3a0aa5bcb83b737dcf33c033b --- /dev/null +++ b/venv/lib/python3.13/site-packages/urllib3/util/ssltransport.py @@ -0,0 +1,271 @@ +from __future__ import annotations + +import io +import socket +import ssl +import typing + +from ..exceptions import ProxySchemeUnsupported + +if typing.TYPE_CHECKING: + from typing_extensions import Self + + from .ssl_ import _TYPE_PEER_CERT_RET, _TYPE_PEER_CERT_RET_DICT + + +_WriteBuffer = typing.Union[bytearray, memoryview] +_ReturnValue = typing.TypeVar("_ReturnValue") + +SSL_BLOCKSIZE = 16384 + + +class SSLTransport: + """ + The SSLTransport wraps an existing socket and establishes an SSL connection. + + Contrary to Python's implementation of SSLSocket, it allows you to chain + multiple TLS connections together. It's particularly useful if you need to + implement TLS within TLS. + + The class supports most of the socket API operations. + """ + + @staticmethod + def _validate_ssl_context_for_tls_in_tls(ssl_context: ssl.SSLContext) -> None: + """ + Raises a ProxySchemeUnsupported if the provided ssl_context can't be used + for TLS in TLS. + + The only requirement is that the ssl_context provides the 'wrap_bio' + methods. + """ + + if not hasattr(ssl_context, "wrap_bio"): + raise ProxySchemeUnsupported( + "TLS in TLS requires SSLContext.wrap_bio() which isn't " + "available on non-native SSLContext" + ) + + def __init__( + self, + socket: socket.socket, + ssl_context: ssl.SSLContext, + server_hostname: str | None = None, + suppress_ragged_eofs: bool = True, + ) -> None: + """ + Create an SSLTransport around socket using the provided ssl_context. + """ + self.incoming = ssl.MemoryBIO() + self.outgoing = ssl.MemoryBIO() + + self.suppress_ragged_eofs = suppress_ragged_eofs + self.socket = socket + + self.sslobj = ssl_context.wrap_bio( + self.incoming, self.outgoing, server_hostname=server_hostname + ) + + # Perform initial handshake. + self._ssl_io_loop(self.sslobj.do_handshake) + + def __enter__(self) -> Self: + return self + + def __exit__(self, *_: typing.Any) -> None: + self.close() + + def fileno(self) -> int: + return self.socket.fileno() + + def read(self, len: int = 1024, buffer: typing.Any | None = None) -> int | bytes: + return self._wrap_ssl_read(len, buffer) + + def recv(self, buflen: int = 1024, flags: int = 0) -> int | bytes: + if flags != 0: + raise ValueError("non-zero flags not allowed in calls to recv") + return self._wrap_ssl_read(buflen) + + def recv_into( + self, + buffer: _WriteBuffer, + nbytes: int | None = None, + flags: int = 0, + ) -> None | int | bytes: + if flags != 0: + raise ValueError("non-zero flags not allowed in calls to recv_into") + if nbytes is None: + nbytes = len(buffer) + return self.read(nbytes, buffer) + + def sendall(self, data: bytes, flags: int = 0) -> None: + if flags != 0: + raise ValueError("non-zero flags not allowed in calls to sendall") + count = 0 + with memoryview(data) as view, view.cast("B") as byte_view: + amount = len(byte_view) + while count < amount: + v = self.send(byte_view[count:]) + count += v + + def send(self, data: bytes, flags: int = 0) -> int: + if flags != 0: + raise ValueError("non-zero flags not allowed in calls to send") + return self._ssl_io_loop(self.sslobj.write, data) + + def makefile( + self, + mode: str, + buffering: int | None = None, + *, + encoding: str | None = None, + errors: str | None = None, + newline: str | None = None, + ) -> typing.BinaryIO | typing.TextIO | socket.SocketIO: + """ + Python's httpclient uses makefile and buffered io when reading HTTP + messages and we need to support it. + + This is unfortunately a copy and paste of socket.py makefile with small + changes to point to the socket directly. + """ + if not set(mode) <= {"r", "w", "b"}: + raise ValueError(f"invalid mode {mode!r} (only r, w, b allowed)") + + writing = "w" in mode + reading = "r" in mode or not writing + assert reading or writing + binary = "b" in mode + rawmode = "" + if reading: + rawmode += "r" + if writing: + rawmode += "w" + raw = socket.SocketIO(self, rawmode) # type: ignore[arg-type] + self.socket._io_refs += 1 # type: ignore[attr-defined] + if buffering is None: + buffering = -1 + if buffering < 0: + buffering = io.DEFAULT_BUFFER_SIZE + if buffering == 0: + if not binary: + raise ValueError("unbuffered streams must be binary") + return raw + buffer: typing.BinaryIO + if reading and writing: + buffer = io.BufferedRWPair(raw, raw, buffering) # type: ignore[assignment] + elif reading: + buffer = io.BufferedReader(raw, buffering) + else: + assert writing + buffer = io.BufferedWriter(raw, buffering) + if binary: + return buffer + text = io.TextIOWrapper(buffer, encoding, errors, newline) + text.mode = mode # type: ignore[misc] + return text + + def unwrap(self) -> None: + self._ssl_io_loop(self.sslobj.unwrap) + + def close(self) -> None: + self.socket.close() + + @typing.overload + def getpeercert( + self, binary_form: typing.Literal[False] = ... + ) -> _TYPE_PEER_CERT_RET_DICT | None: ... + + @typing.overload + def getpeercert(self, binary_form: typing.Literal[True]) -> bytes | None: ... + + def getpeercert(self, binary_form: bool = False) -> _TYPE_PEER_CERT_RET: + return self.sslobj.getpeercert(binary_form) # type: ignore[return-value] + + def version(self) -> str | None: + return self.sslobj.version() + + def cipher(self) -> tuple[str, str, int] | None: + return self.sslobj.cipher() + + def selected_alpn_protocol(self) -> str | None: + return self.sslobj.selected_alpn_protocol() + + def shared_ciphers(self) -> list[tuple[str, str, int]] | None: + return self.sslobj.shared_ciphers() + + def compression(self) -> str | None: + return self.sslobj.compression() + + def settimeout(self, value: float | None) -> None: + self.socket.settimeout(value) + + def gettimeout(self) -> float | None: + return self.socket.gettimeout() + + def _decref_socketios(self) -> None: + self.socket._decref_socketios() # type: ignore[attr-defined] + + def _wrap_ssl_read(self, len: int, buffer: bytearray | None = None) -> int | bytes: + try: + return self._ssl_io_loop(self.sslobj.read, len, buffer) + except ssl.SSLError as e: + if e.errno == ssl.SSL_ERROR_EOF and self.suppress_ragged_eofs: + return 0 # eof, return 0. + else: + raise + + # func is sslobj.do_handshake or sslobj.unwrap + @typing.overload + def _ssl_io_loop(self, func: typing.Callable[[], None]) -> None: ... + + # func is sslobj.write, arg1 is data + @typing.overload + def _ssl_io_loop(self, func: typing.Callable[[bytes], int], arg1: bytes) -> int: ... + + # func is sslobj.read, arg1 is len, arg2 is buffer + @typing.overload + def _ssl_io_loop( + self, + func: typing.Callable[[int, bytearray | None], bytes], + arg1: int, + arg2: bytearray | None, + ) -> bytes: ... + + def _ssl_io_loop( + self, + func: typing.Callable[..., _ReturnValue], + arg1: None | bytes | int = None, + arg2: bytearray | None = None, + ) -> _ReturnValue: + """Performs an I/O loop between incoming/outgoing and the socket.""" + should_loop = True + ret = None + + while should_loop: + errno = None + try: + if arg1 is None and arg2 is None: + ret = func() + elif arg2 is None: + ret = func(arg1) + else: + ret = func(arg1, arg2) + except ssl.SSLError as e: + if e.errno not in (ssl.SSL_ERROR_WANT_READ, ssl.SSL_ERROR_WANT_WRITE): + # WANT_READ, and WANT_WRITE are expected, others are not. + raise e + errno = e.errno + + buf = self.outgoing.read() + self.socket.sendall(buf) + + if errno is None: + should_loop = False + elif errno == ssl.SSL_ERROR_WANT_READ: + buf = self.socket.recv(SSL_BLOCKSIZE) + if buf: + self.incoming.write(buf) + else: + self.incoming.write_eof() + return typing.cast(_ReturnValue, ret) diff --git a/venv/lib/python3.13/site-packages/urllib3/util/timeout.py b/venv/lib/python3.13/site-packages/urllib3/util/timeout.py new file mode 100644 index 0000000000000000000000000000000000000000..4bb1be11d9cb06900dd82ecebd06aa6a7c5de916 --- /dev/null +++ b/venv/lib/python3.13/site-packages/urllib3/util/timeout.py @@ -0,0 +1,275 @@ +from __future__ import annotations + +import time +import typing +from enum import Enum +from socket import getdefaulttimeout + +from ..exceptions import TimeoutStateError + +if typing.TYPE_CHECKING: + from typing import Final + + +class _TYPE_DEFAULT(Enum): + # This value should never be passed to socket.settimeout() so for safety we use a -1. + # socket.settimout() raises a ValueError for negative values. + token = -1 + + +_DEFAULT_TIMEOUT: Final[_TYPE_DEFAULT] = _TYPE_DEFAULT.token + +_TYPE_TIMEOUT = typing.Optional[typing.Union[float, _TYPE_DEFAULT]] + + +class Timeout: + """Timeout configuration. + + Timeouts can be defined as a default for a pool: + + .. code-block:: python + + import urllib3 + + timeout = urllib3.util.Timeout(connect=2.0, read=7.0) + + http = urllib3.PoolManager(timeout=timeout) + + resp = http.request("GET", "https://example.com/") + + print(resp.status) + + Or per-request (which overrides the default for the pool): + + .. code-block:: python + + response = http.request("GET", "https://example.com/", timeout=Timeout(10)) + + Timeouts can be disabled by setting all the parameters to ``None``: + + .. code-block:: python + + no_timeout = Timeout(connect=None, read=None) + response = http.request("GET", "https://example.com/", timeout=no_timeout) + + + :param total: + This combines the connect and read timeouts into one; the read timeout + will be set to the time leftover from the connect attempt. In the + event that both a connect timeout and a total are specified, or a read + timeout and a total are specified, the shorter timeout will be applied. + + Defaults to None. + + :type total: int, float, or None + + :param connect: + The maximum amount of time (in seconds) to wait for a connection + attempt to a server to succeed. Omitting the parameter will default the + connect timeout to the system default, probably `the global default + timeout in socket.py + `_. + None will set an infinite timeout for connection attempts. + + :type connect: int, float, or None + + :param read: + The maximum amount of time (in seconds) to wait between consecutive + read operations for a response from the server. Omitting the parameter + will default the read timeout to the system default, probably `the + global default timeout in socket.py + `_. + None will set an infinite timeout. + + :type read: int, float, or None + + .. note:: + + Many factors can affect the total amount of time for urllib3 to return + an HTTP response. + + For example, Python's DNS resolver does not obey the timeout specified + on the socket. Other factors that can affect total request time include + high CPU load, high swap, the program running at a low priority level, + or other behaviors. + + In addition, the read and total timeouts only measure the time between + read operations on the socket connecting the client and the server, + not the total amount of time for the request to return a complete + response. For most requests, the timeout is raised because the server + has not sent the first byte in the specified time. This is not always + the case; if a server streams one byte every fifteen seconds, a timeout + of 20 seconds will not trigger, even though the request will take + several minutes to complete. + """ + + #: A sentinel object representing the default timeout value + DEFAULT_TIMEOUT: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT + + def __init__( + self, + total: _TYPE_TIMEOUT = None, + connect: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + read: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, + ) -> None: + self._connect = self._validate_timeout(connect, "connect") + self._read = self._validate_timeout(read, "read") + self.total = self._validate_timeout(total, "total") + self._start_connect: float | None = None + + def __repr__(self) -> str: + return f"{type(self).__name__}(connect={self._connect!r}, read={self._read!r}, total={self.total!r})" + + # __str__ provided for backwards compatibility + __str__ = __repr__ + + @staticmethod + def resolve_default_timeout(timeout: _TYPE_TIMEOUT) -> float | None: + return getdefaulttimeout() if timeout is _DEFAULT_TIMEOUT else timeout + + @classmethod + def _validate_timeout(cls, value: _TYPE_TIMEOUT, name: str) -> _TYPE_TIMEOUT: + """Check that a timeout attribute is valid. + + :param value: The timeout value to validate + :param name: The name of the timeout attribute to validate. This is + used to specify in error messages. + :return: The validated and casted version of the given value. + :raises ValueError: If it is a numeric value less than or equal to + zero, or the type is not an integer, float, or None. + """ + if value is None or value is _DEFAULT_TIMEOUT: + return value + + if isinstance(value, bool): + raise ValueError( + "Timeout cannot be a boolean value. It must " + "be an int, float or None." + ) + try: + float(value) + except (TypeError, ValueError): + raise ValueError( + "Timeout value %s was %s, but it must be an " + "int, float or None." % (name, value) + ) from None + + try: + if value <= 0: + raise ValueError( + "Attempted to set %s timeout to %s, but the " + "timeout cannot be set to a value less " + "than or equal to 0." % (name, value) + ) + except TypeError: + raise ValueError( + "Timeout value %s was %s, but it must be an " + "int, float or None." % (name, value) + ) from None + + return value + + @classmethod + def from_float(cls, timeout: _TYPE_TIMEOUT) -> Timeout: + """Create a new Timeout from a legacy timeout value. + + The timeout value used by httplib.py sets the same timeout on the + connect(), and recv() socket requests. This creates a :class:`Timeout` + object that sets the individual timeouts to the ``timeout`` value + passed to this function. + + :param timeout: The legacy timeout value. + :type timeout: integer, float, :attr:`urllib3.util.Timeout.DEFAULT_TIMEOUT`, or None + :return: Timeout object + :rtype: :class:`Timeout` + """ + return Timeout(read=timeout, connect=timeout) + + def clone(self) -> Timeout: + """Create a copy of the timeout object + + Timeout properties are stored per-pool but each request needs a fresh + Timeout object to ensure each one has its own start/stop configured. + + :return: a copy of the timeout object + :rtype: :class:`Timeout` + """ + # We can't use copy.deepcopy because that will also create a new object + # for _GLOBAL_DEFAULT_TIMEOUT, which socket.py uses as a sentinel to + # detect the user default. + return Timeout(connect=self._connect, read=self._read, total=self.total) + + def start_connect(self) -> float: + """Start the timeout clock, used during a connect() attempt + + :raises urllib3.exceptions.TimeoutStateError: if you attempt + to start a timer that has been started already. + """ + if self._start_connect is not None: + raise TimeoutStateError("Timeout timer has already been started.") + self._start_connect = time.monotonic() + return self._start_connect + + def get_connect_duration(self) -> float: + """Gets the time elapsed since the call to :meth:`start_connect`. + + :return: Elapsed time in seconds. + :rtype: float + :raises urllib3.exceptions.TimeoutStateError: if you attempt + to get duration for a timer that hasn't been started. + """ + if self._start_connect is None: + raise TimeoutStateError( + "Can't get connect duration for timer that has not started." + ) + return time.monotonic() - self._start_connect + + @property + def connect_timeout(self) -> _TYPE_TIMEOUT: + """Get the value to use when setting a connection timeout. + + This will be a positive float or integer, the value None + (never timeout), or the default system timeout. + + :return: Connect timeout. + :rtype: int, float, :attr:`Timeout.DEFAULT_TIMEOUT` or None + """ + if self.total is None: + return self._connect + + if self._connect is None or self._connect is _DEFAULT_TIMEOUT: + return self.total + + return min(self._connect, self.total) # type: ignore[type-var] + + @property + def read_timeout(self) -> float | None: + """Get the value for the read timeout. + + This assumes some time has elapsed in the connection timeout and + computes the read timeout appropriately. + + If self.total is set, the read timeout is dependent on the amount of + time taken by the connect timeout. If the connection time has not been + established, a :exc:`~urllib3.exceptions.TimeoutStateError` will be + raised. + + :return: Value to use for the read timeout. + :rtype: int, float or None + :raises urllib3.exceptions.TimeoutStateError: If :meth:`start_connect` + has not yet been called on this object. + """ + if ( + self.total is not None + and self.total is not _DEFAULT_TIMEOUT + and self._read is not None + and self._read is not _DEFAULT_TIMEOUT + ): + # In case the connect timeout has not yet been established. + if self._start_connect is None: + return self._read + return max(0, min(self.total - self.get_connect_duration(), self._read)) + elif self.total is not None and self.total is not _DEFAULT_TIMEOUT: + return max(0, self.total - self.get_connect_duration()) + else: + return self.resolve_default_timeout(self._read) diff --git a/venv/lib/python3.13/site-packages/urllib3/util/url.py b/venv/lib/python3.13/site-packages/urllib3/util/url.py new file mode 100644 index 0000000000000000000000000000000000000000..db057f17be610174f30928748b5004dcbf6c501c --- /dev/null +++ b/venv/lib/python3.13/site-packages/urllib3/util/url.py @@ -0,0 +1,469 @@ +from __future__ import annotations + +import re +import typing + +from ..exceptions import LocationParseError +from .util import to_str + +# We only want to normalize urls with an HTTP(S) scheme. +# urllib3 infers URLs without a scheme (None) to be http. +_NORMALIZABLE_SCHEMES = ("http", "https", None) + +# Almost all of these patterns were derived from the +# 'rfc3986' module: https://github.com/python-hyper/rfc3986 +_PERCENT_RE = re.compile(r"%[a-fA-F0-9]{2}") +_SCHEME_RE = re.compile(r"^(?:[a-zA-Z][a-zA-Z0-9+-]*:|/)") +_URI_RE = re.compile( + r"^(?:([a-zA-Z][a-zA-Z0-9+.-]*):)?" + r"(?://([^\\/?#]*))?" + r"([^?#]*)" + r"(?:\?([^#]*))?" + r"(?:#(.*))?$", + re.UNICODE | re.DOTALL, +) + +_IPV4_PAT = r"(?:[0-9]{1,3}\.){3}[0-9]{1,3}" +_HEX_PAT = "[0-9A-Fa-f]{1,4}" +_LS32_PAT = "(?:{hex}:{hex}|{ipv4})".format(hex=_HEX_PAT, ipv4=_IPV4_PAT) +_subs = {"hex": _HEX_PAT, "ls32": _LS32_PAT} +_variations = [ + # 6( h16 ":" ) ls32 + "(?:%(hex)s:){6}%(ls32)s", + # "::" 5( h16 ":" ) ls32 + "::(?:%(hex)s:){5}%(ls32)s", + # [ h16 ] "::" 4( h16 ":" ) ls32 + "(?:%(hex)s)?::(?:%(hex)s:){4}%(ls32)s", + # [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32 + "(?:(?:%(hex)s:)?%(hex)s)?::(?:%(hex)s:){3}%(ls32)s", + # [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32 + "(?:(?:%(hex)s:){0,2}%(hex)s)?::(?:%(hex)s:){2}%(ls32)s", + # [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32 + "(?:(?:%(hex)s:){0,3}%(hex)s)?::%(hex)s:%(ls32)s", + # [ *4( h16 ":" ) h16 ] "::" ls32 + "(?:(?:%(hex)s:){0,4}%(hex)s)?::%(ls32)s", + # [ *5( h16 ":" ) h16 ] "::" h16 + "(?:(?:%(hex)s:){0,5}%(hex)s)?::%(hex)s", + # [ *6( h16 ":" ) h16 ] "::" + "(?:(?:%(hex)s:){0,6}%(hex)s)?::", +] + +_UNRESERVED_PAT = r"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._\-~" +_IPV6_PAT = "(?:" + "|".join([x % _subs for x in _variations]) + ")" +_ZONE_ID_PAT = "(?:%25|%)(?:[" + _UNRESERVED_PAT + "]|%[a-fA-F0-9]{2})+" +_IPV6_ADDRZ_PAT = r"\[" + _IPV6_PAT + r"(?:" + _ZONE_ID_PAT + r")?\]" +_REG_NAME_PAT = r"(?:[^\[\]%:/?#]|%[a-fA-F0-9]{2})*" +_TARGET_RE = re.compile(r"^(/[^?#]*)(?:\?([^#]*))?(?:#.*)?$") + +_IPV4_RE = re.compile("^" + _IPV4_PAT + "$") +_IPV6_RE = re.compile("^" + _IPV6_PAT + "$") +_IPV6_ADDRZ_RE = re.compile("^" + _IPV6_ADDRZ_PAT + "$") +_BRACELESS_IPV6_ADDRZ_RE = re.compile("^" + _IPV6_ADDRZ_PAT[2:-2] + "$") +_ZONE_ID_RE = re.compile("(" + _ZONE_ID_PAT + r")\]$") + +_HOST_PORT_PAT = ("^(%s|%s|%s)(?::0*?(|0|[1-9][0-9]{0,4}))?$") % ( + _REG_NAME_PAT, + _IPV4_PAT, + _IPV6_ADDRZ_PAT, +) +_HOST_PORT_RE = re.compile(_HOST_PORT_PAT, re.UNICODE | re.DOTALL) + +_UNRESERVED_CHARS = set( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-~" +) +_SUB_DELIM_CHARS = set("!$&'()*+,;=") +_USERINFO_CHARS = _UNRESERVED_CHARS | _SUB_DELIM_CHARS | {":"} +_PATH_CHARS = _USERINFO_CHARS | {"@", "/"} +_QUERY_CHARS = _FRAGMENT_CHARS = _PATH_CHARS | {"?"} + + +class Url( + typing.NamedTuple( + "Url", + [ + ("scheme", typing.Optional[str]), + ("auth", typing.Optional[str]), + ("host", typing.Optional[str]), + ("port", typing.Optional[int]), + ("path", typing.Optional[str]), + ("query", typing.Optional[str]), + ("fragment", typing.Optional[str]), + ], + ) +): + """ + Data structure for representing an HTTP URL. Used as a return value for + :func:`parse_url`. Both the scheme and host are normalized as they are + both case-insensitive according to RFC 3986. + """ + + def __new__( # type: ignore[no-untyped-def] + cls, + scheme: str | None = None, + auth: str | None = None, + host: str | None = None, + port: int | None = None, + path: str | None = None, + query: str | None = None, + fragment: str | None = None, + ): + if path and not path.startswith("/"): + path = "/" + path + if scheme is not None: + scheme = scheme.lower() + return super().__new__(cls, scheme, auth, host, port, path, query, fragment) + + @property + def hostname(self) -> str | None: + """For backwards-compatibility with urlparse. We're nice like that.""" + return self.host + + @property + def request_uri(self) -> str: + """Absolute path including the query string.""" + uri = self.path or "/" + + if self.query is not None: + uri += "?" + self.query + + return uri + + @property + def authority(self) -> str | None: + """ + Authority component as defined in RFC 3986 3.2. + This includes userinfo (auth), host and port. + + i.e. + userinfo@host:port + """ + userinfo = self.auth + netloc = self.netloc + if netloc is None or userinfo is None: + return netloc + else: + return f"{userinfo}@{netloc}" + + @property + def netloc(self) -> str | None: + """ + Network location including host and port. + + If you need the equivalent of urllib.parse's ``netloc``, + use the ``authority`` property instead. + """ + if self.host is None: + return None + if self.port: + return f"{self.host}:{self.port}" + return self.host + + @property + def url(self) -> str: + """ + Convert self into a url + + This function should more or less round-trip with :func:`.parse_url`. The + returned url may not be exactly the same as the url inputted to + :func:`.parse_url`, but it should be equivalent by the RFC (e.g., urls + with a blank port will have : removed). + + Example: + + .. code-block:: python + + import urllib3 + + U = urllib3.util.parse_url("https://google.com/mail/") + + print(U.url) + # "https://google.com/mail/" + + print( urllib3.util.Url("https", "username:password", + "host.com", 80, "/path", "query", "fragment" + ).url + ) + # "https://username:password@host.com:80/path?query#fragment" + """ + scheme, auth, host, port, path, query, fragment = self + url = "" + + # We use "is not None" we want things to happen with empty strings (or 0 port) + if scheme is not None: + url += scheme + "://" + if auth is not None: + url += auth + "@" + if host is not None: + url += host + if port is not None: + url += ":" + str(port) + if path is not None: + url += path + if query is not None: + url += "?" + query + if fragment is not None: + url += "#" + fragment + + return url + + def __str__(self) -> str: + return self.url + + +@typing.overload +def _encode_invalid_chars( + component: str, allowed_chars: typing.Container[str] +) -> str: # Abstract + ... + + +@typing.overload +def _encode_invalid_chars( + component: None, allowed_chars: typing.Container[str] +) -> None: # Abstract + ... + + +def _encode_invalid_chars( + component: str | None, allowed_chars: typing.Container[str] +) -> str | None: + """Percent-encodes a URI component without reapplying + onto an already percent-encoded component. + """ + if component is None: + return component + + component = to_str(component) + + # Normalize existing percent-encoded bytes. + # Try to see if the component we're encoding is already percent-encoded + # so we can skip all '%' characters but still encode all others. + component, percent_encodings = _PERCENT_RE.subn( + lambda match: match.group(0).upper(), component + ) + + uri_bytes = component.encode("utf-8", "surrogatepass") + is_percent_encoded = percent_encodings == uri_bytes.count(b"%") + encoded_component = bytearray() + + for i in range(0, len(uri_bytes)): + # Will return a single character bytestring + byte = uri_bytes[i : i + 1] + byte_ord = ord(byte) + if (is_percent_encoded and byte == b"%") or ( + byte_ord < 128 and byte.decode() in allowed_chars + ): + encoded_component += byte + continue + encoded_component.extend(b"%" + (hex(byte_ord)[2:].encode().zfill(2).upper())) + + return encoded_component.decode() + + +def _remove_path_dot_segments(path: str) -> str: + # See http://tools.ietf.org/html/rfc3986#section-5.2.4 for pseudo-code + segments = path.split("/") # Turn the path into a list of segments + output = [] # Initialize the variable to use to store output + + for segment in segments: + # '.' is the current directory, so ignore it, it is superfluous + if segment == ".": + continue + # Anything other than '..', should be appended to the output + if segment != "..": + output.append(segment) + # In this case segment == '..', if we can, we should pop the last + # element + elif output: + output.pop() + + # If the path starts with '/' and the output is empty or the first string + # is non-empty + if path.startswith("/") and (not output or output[0]): + output.insert(0, "") + + # If the path starts with '/.' or '/..' ensure we add one more empty + # string to add a trailing '/' + if path.endswith(("/.", "/..")): + output.append("") + + return "/".join(output) + + +@typing.overload +def _normalize_host(host: None, scheme: str | None) -> None: ... + + +@typing.overload +def _normalize_host(host: str, scheme: str | None) -> str: ... + + +def _normalize_host(host: str | None, scheme: str | None) -> str | None: + if host: + if scheme in _NORMALIZABLE_SCHEMES: + is_ipv6 = _IPV6_ADDRZ_RE.match(host) + if is_ipv6: + # IPv6 hosts of the form 'a::b%zone' are encoded in a URL as + # such per RFC 6874: 'a::b%25zone'. Unquote the ZoneID + # separator as necessary to return a valid RFC 4007 scoped IP. + match = _ZONE_ID_RE.search(host) + if match: + start, end = match.span(1) + zone_id = host[start:end] + + if zone_id.startswith("%25") and zone_id != "%25": + zone_id = zone_id[3:] + else: + zone_id = zone_id[1:] + zone_id = _encode_invalid_chars(zone_id, _UNRESERVED_CHARS) + return f"{host[:start].lower()}%{zone_id}{host[end:]}" + else: + return host.lower() + elif not _IPV4_RE.match(host): + return to_str( + b".".join([_idna_encode(label) for label in host.split(".")]), + "ascii", + ) + return host + + +def _idna_encode(name: str) -> bytes: + if not name.isascii(): + try: + import idna + except ImportError: + raise LocationParseError( + "Unable to parse URL without the 'idna' module" + ) from None + + try: + return idna.encode(name.lower(), strict=True, std3_rules=True) + except idna.IDNAError: + raise LocationParseError( + f"Name '{name}' is not a valid IDNA label" + ) from None + + return name.lower().encode("ascii") + + +def _encode_target(target: str) -> str: + """Percent-encodes a request target so that there are no invalid characters + + Pre-condition for this function is that 'target' must start with '/'. + If that is the case then _TARGET_RE will always produce a match. + """ + match = _TARGET_RE.match(target) + if not match: # Defensive: + raise LocationParseError(f"{target!r} is not a valid request URI") + + path, query = match.groups() + encoded_target = _encode_invalid_chars(path, _PATH_CHARS) + if query is not None: + query = _encode_invalid_chars(query, _QUERY_CHARS) + encoded_target += "?" + query + return encoded_target + + +def parse_url(url: str) -> Url: + """ + Given a url, return a parsed :class:`.Url` namedtuple. Best-effort is + performed to parse incomplete urls. Fields not provided will be None. + This parser is RFC 3986 and RFC 6874 compliant. + + The parser logic and helper functions are based heavily on + work done in the ``rfc3986`` module. + + :param str url: URL to parse into a :class:`.Url` namedtuple. + + Partly backwards-compatible with :mod:`urllib.parse`. + + Example: + + .. code-block:: python + + import urllib3 + + print( urllib3.util.parse_url('http://google.com/mail/')) + # Url(scheme='http', host='google.com', port=None, path='/mail/', ...) + + print( urllib3.util.parse_url('google.com:80')) + # Url(scheme=None, host='google.com', port=80, path=None, ...) + + print( urllib3.util.parse_url('/foo?bar')) + # Url(scheme=None, host=None, port=None, path='/foo', query='bar', ...) + """ + if not url: + # Empty + return Url() + + source_url = url + if not _SCHEME_RE.search(url): + url = "//" + url + + scheme: str | None + authority: str | None + auth: str | None + host: str | None + port: str | None + port_int: int | None + path: str | None + query: str | None + fragment: str | None + + try: + scheme, authority, path, query, fragment = _URI_RE.match(url).groups() # type: ignore[union-attr] + normalize_uri = scheme is None or scheme.lower() in _NORMALIZABLE_SCHEMES + + if scheme: + scheme = scheme.lower() + + if authority: + auth, _, host_port = authority.rpartition("@") + auth = auth or None + host, port = _HOST_PORT_RE.match(host_port).groups() # type: ignore[union-attr] + if auth and normalize_uri: + auth = _encode_invalid_chars(auth, _USERINFO_CHARS) + if port == "": + port = None + else: + auth, host, port = None, None, None + + if port is not None: + port_int = int(port) + if not (0 <= port_int <= 65535): + raise LocationParseError(url) + else: + port_int = None + + host = _normalize_host(host, scheme) + + if normalize_uri and path: + path = _remove_path_dot_segments(path) + path = _encode_invalid_chars(path, _PATH_CHARS) + if normalize_uri and query: + query = _encode_invalid_chars(query, _QUERY_CHARS) + if normalize_uri and fragment: + fragment = _encode_invalid_chars(fragment, _FRAGMENT_CHARS) + + except (ValueError, AttributeError) as e: + raise LocationParseError(source_url) from e + + # For the sake of backwards compatibility we put empty + # string values for path if there are any defined values + # beyond the path in the URL. + # TODO: Remove this when we break backwards compatibility. + if not path: + if query is not None or fragment is not None: + path = "" + else: + path = None + + return Url( + scheme=scheme, + auth=auth, + host=host, + port=port_int, + path=path, + query=query, + fragment=fragment, + ) diff --git a/venv/lib/python3.13/site-packages/urllib3/util/util.py b/venv/lib/python3.13/site-packages/urllib3/util/util.py new file mode 100644 index 0000000000000000000000000000000000000000..35c77e4025842f548565334a3c04cba90f9283d6 --- /dev/null +++ b/venv/lib/python3.13/site-packages/urllib3/util/util.py @@ -0,0 +1,42 @@ +from __future__ import annotations + +import typing +from types import TracebackType + + +def to_bytes( + x: str | bytes, encoding: str | None = None, errors: str | None = None +) -> bytes: + if isinstance(x, bytes): + return x + elif not isinstance(x, str): + raise TypeError(f"not expecting type {type(x).__name__}") + if encoding or errors: + return x.encode(encoding or "utf-8", errors=errors or "strict") + return x.encode() + + +def to_str( + x: str | bytes, encoding: str | None = None, errors: str | None = None +) -> str: + if isinstance(x, str): + return x + elif not isinstance(x, bytes): + raise TypeError(f"not expecting type {type(x).__name__}") + if encoding or errors: + return x.decode(encoding or "utf-8", errors=errors or "strict") + return x.decode() + + +def reraise( + tp: type[BaseException] | None, + value: BaseException, + tb: TracebackType | None = None, +) -> typing.NoReturn: + try: + if value.__traceback__ is not tb: + raise value.with_traceback(tb) + raise value + finally: + value = None # type: ignore[assignment] + tb = None diff --git a/venv/lib/python3.13/site-packages/urllib3/util/wait.py b/venv/lib/python3.13/site-packages/urllib3/util/wait.py new file mode 100644 index 0000000000000000000000000000000000000000..aeca0c7ad5b232eeb1ad9c43d315bd1d74eaed9a --- /dev/null +++ b/venv/lib/python3.13/site-packages/urllib3/util/wait.py @@ -0,0 +1,124 @@ +from __future__ import annotations + +import select +import socket +from functools import partial + +__all__ = ["wait_for_read", "wait_for_write"] + + +# How should we wait on sockets? +# +# There are two types of APIs you can use for waiting on sockets: the fancy +# modern stateful APIs like epoll/kqueue, and the older stateless APIs like +# select/poll. The stateful APIs are more efficient when you have a lots of +# sockets to keep track of, because you can set them up once and then use them +# lots of times. But we only ever want to wait on a single socket at a time +# and don't want to keep track of state, so the stateless APIs are actually +# more efficient. So we want to use select() or poll(). +# +# Now, how do we choose between select() and poll()? On traditional Unixes, +# select() has a strange calling convention that makes it slow, or fail +# altogether, for high-numbered file descriptors. The point of poll() is to fix +# that, so on Unixes, we prefer poll(). +# +# On Windows, there is no poll() (or at least Python doesn't provide a wrapper +# for it), but that's OK, because on Windows, select() doesn't have this +# strange calling convention; plain select() works fine. +# +# So: on Windows we use select(), and everywhere else we use poll(). We also +# fall back to select() in case poll() is somehow broken or missing. + + +def select_wait_for_socket( + sock: socket.socket, + read: bool = False, + write: bool = False, + timeout: float | None = None, +) -> bool: + if not read and not write: + raise RuntimeError("must specify at least one of read=True, write=True") + rcheck = [] + wcheck = [] + if read: + rcheck.append(sock) + if write: + wcheck.append(sock) + # When doing a non-blocking connect, most systems signal success by + # marking the socket writable. Windows, though, signals success by marked + # it as "exceptional". We paper over the difference by checking the write + # sockets for both conditions. (The stdlib selectors module does the same + # thing.) + fn = partial(select.select, rcheck, wcheck, wcheck) + rready, wready, xready = fn(timeout) + return bool(rready or wready or xready) + + +def poll_wait_for_socket( + sock: socket.socket, + read: bool = False, + write: bool = False, + timeout: float | None = None, +) -> bool: + if not read and not write: + raise RuntimeError("must specify at least one of read=True, write=True") + mask = 0 + if read: + mask |= select.POLLIN + if write: + mask |= select.POLLOUT + poll_obj = select.poll() + poll_obj.register(sock, mask) + + # For some reason, poll() takes timeout in milliseconds + def do_poll(t: float | None) -> list[tuple[int, int]]: + if t is not None: + t *= 1000 + return poll_obj.poll(t) + + return bool(do_poll(timeout)) + + +def _have_working_poll() -> bool: + # Apparently some systems have a select.poll that fails as soon as you try + # to use it, either due to strange configuration or broken monkeypatching + # from libraries like eventlet/greenlet. + try: + poll_obj = select.poll() + poll_obj.poll(0) + except (AttributeError, OSError): + return False + else: + return True + + +def wait_for_socket( + sock: socket.socket, + read: bool = False, + write: bool = False, + timeout: float | None = None, +) -> bool: + # We delay choosing which implementation to use until the first time we're + # called. We could do it at import time, but then we might make the wrong + # decision if someone goes wild with monkeypatching select.poll after + # we're imported. + global wait_for_socket + if _have_working_poll(): + wait_for_socket = poll_wait_for_socket + elif hasattr(select, "select"): + wait_for_socket = select_wait_for_socket + return wait_for_socket(sock, read, write, timeout) + + +def wait_for_read(sock: socket.socket, timeout: float | None = None) -> bool: + """Waits for reading to be available on a given socket. + Returns True if the socket is readable, or False if the timeout expired. + """ + return wait_for_socket(sock, read=True, timeout=timeout) + + +def wait_for_write(sock: socket.socket, timeout: float | None = None) -> bool: + """Waits for writing to be available on a given socket. + Returns True if the socket is readable, or False if the timeout expired. + """ + return wait_for_socket(sock, write=True, timeout=timeout)