Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .venv/lib/python3.11/site-packages/distro/__init__.py +54 -0
- .venv/lib/python3.11/site-packages/distro/__main__.py +4 -0
- .venv/lib/python3.11/site-packages/distro/__pycache__/__init__.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/distro/__pycache__/__main__.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/distro/__pycache__/distro.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/distro/distro.py +1403 -0
- .venv/lib/python3.11/site-packages/distro/py.typed +0 -0
- .venv/lib/python3.11/site-packages/gguf-0.10.0.dist-info/INSTALLER +1 -0
- .venv/lib/python3.11/site-packages/gguf-0.10.0.dist-info/LICENSE +21 -0
- .venv/lib/python3.11/site-packages/gguf-0.10.0.dist-info/METADATA +114 -0
- .venv/lib/python3.11/site-packages/gguf-0.10.0.dist-info/RECORD +45 -0
- .venv/lib/python3.11/site-packages/gguf-0.10.0.dist-info/WHEEL +4 -0
- .venv/lib/python3.11/site-packages/gguf-0.10.0.dist-info/entry_points.txt +6 -0
- .venv/lib/python3.11/site-packages/mistral_common/__init__.py +1 -0
- .venv/lib/python3.11/site-packages/mistral_common/base.py +9 -0
- .venv/lib/python3.11/site-packages/mistral_common/exceptions.py +67 -0
- .venv/lib/python3.11/site-packages/mistral_common/multimodal.py +70 -0
- .venv/lib/python3.11/site-packages/mistral_common/protocol/__init__.py +0 -0
- .venv/lib/python3.11/site-packages/mistral_common/protocol/__pycache__/__init__.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/mistral_common/protocol/__pycache__/base.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/mistral_common/protocol/__pycache__/utils.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/mistral_common/protocol/base.py +18 -0
- .venv/lib/python3.11/site-packages/mistral_common/protocol/instruct/__init__.py +0 -0
- .venv/lib/python3.11/site-packages/mistral_common/protocol/instruct/__pycache__/normalize.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/mistral_common/protocol/instruct/__pycache__/request.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/mistral_common/protocol/instruct/__pycache__/tool_calls.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/mistral_common/protocol/instruct/__pycache__/validator.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/mistral_common/protocol/instruct/messages.py +113 -0
- .venv/lib/python3.11/site-packages/mistral_common/protocol/instruct/normalize.py +265 -0
- .venv/lib/python3.11/site-packages/mistral_common/protocol/instruct/request.py +27 -0
- .venv/lib/python3.11/site-packages/mistral_common/protocol/instruct/response.py +66 -0
- .venv/lib/python3.11/site-packages/mistral_common/protocol/instruct/tool_calls.py +51 -0
- .venv/lib/python3.11/site-packages/mistral_common/protocol/instruct/validator.py +328 -0
- .venv/lib/python3.11/site-packages/mistral_common/protocol/utils.py +5 -0
- .venv/lib/python3.11/site-packages/mistral_common/py.typed +0 -0
- .venv/lib/python3.11/site-packages/numpy/polynomial/tests/__pycache__/__init__.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/numpy/polynomial/tests/__pycache__/test_hermite_e.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/vllm-0.7.2.dist-info/INSTALLER +1 -0
- .venv/lib/python3.11/site-packages/vllm-0.7.2.dist-info/LICENSE +201 -0
- .venv/lib/python3.11/site-packages/vllm-0.7.2.dist-info/METADATA +238 -0
- .venv/lib/python3.11/site-packages/vllm-0.7.2.dist-info/RECORD +0 -0
- .venv/lib/python3.11/site-packages/vllm-0.7.2.dist-info/REQUESTED +0 -0
- .venv/lib/python3.11/site-packages/vllm-0.7.2.dist-info/WHEEL +5 -0
- .venv/lib/python3.11/site-packages/vllm-0.7.2.dist-info/entry_points.txt +2 -0
- .venv/lib/python3.11/site-packages/vllm-0.7.2.dist-info/top_level.txt +1 -0
- .venv/lib/python3.11/site-packages/xgrammar/__init__.py +13 -0
- .venv/lib/python3.11/site-packages/xgrammar/__pycache__/__init__.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/xgrammar/__pycache__/base.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/xgrammar/__pycache__/compiler.cpython-311.pyc +0 -0
- .venv/lib/python3.11/site-packages/xgrammar/__pycache__/grammar.cpython-311.pyc +0 -0
.venv/lib/python3.11/site-packages/distro/__init__.py
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .distro import (
|
| 2 |
+
NORMALIZED_DISTRO_ID,
|
| 3 |
+
NORMALIZED_LSB_ID,
|
| 4 |
+
NORMALIZED_OS_ID,
|
| 5 |
+
LinuxDistribution,
|
| 6 |
+
__version__,
|
| 7 |
+
build_number,
|
| 8 |
+
codename,
|
| 9 |
+
distro_release_attr,
|
| 10 |
+
distro_release_info,
|
| 11 |
+
id,
|
| 12 |
+
info,
|
| 13 |
+
like,
|
| 14 |
+
linux_distribution,
|
| 15 |
+
lsb_release_attr,
|
| 16 |
+
lsb_release_info,
|
| 17 |
+
major_version,
|
| 18 |
+
minor_version,
|
| 19 |
+
name,
|
| 20 |
+
os_release_attr,
|
| 21 |
+
os_release_info,
|
| 22 |
+
uname_attr,
|
| 23 |
+
uname_info,
|
| 24 |
+
version,
|
| 25 |
+
version_parts,
|
| 26 |
+
)
|
| 27 |
+
|
| 28 |
+
__all__ = [
|
| 29 |
+
"NORMALIZED_DISTRO_ID",
|
| 30 |
+
"NORMALIZED_LSB_ID",
|
| 31 |
+
"NORMALIZED_OS_ID",
|
| 32 |
+
"LinuxDistribution",
|
| 33 |
+
"build_number",
|
| 34 |
+
"codename",
|
| 35 |
+
"distro_release_attr",
|
| 36 |
+
"distro_release_info",
|
| 37 |
+
"id",
|
| 38 |
+
"info",
|
| 39 |
+
"like",
|
| 40 |
+
"linux_distribution",
|
| 41 |
+
"lsb_release_attr",
|
| 42 |
+
"lsb_release_info",
|
| 43 |
+
"major_version",
|
| 44 |
+
"minor_version",
|
| 45 |
+
"name",
|
| 46 |
+
"os_release_attr",
|
| 47 |
+
"os_release_info",
|
| 48 |
+
"uname_attr",
|
| 49 |
+
"uname_info",
|
| 50 |
+
"version",
|
| 51 |
+
"version_parts",
|
| 52 |
+
]
|
| 53 |
+
|
| 54 |
+
__version__ = __version__
|
.venv/lib/python3.11/site-packages/distro/__main__.py
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .distro import main
|
| 2 |
+
|
| 3 |
+
if __name__ == "__main__":
|
| 4 |
+
main()
|
.venv/lib/python3.11/site-packages/distro/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (1.18 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/distro/__pycache__/__main__.cpython-311.pyc
ADDED
|
Binary file (312 Bytes). View file
|
|
|
.venv/lib/python3.11/site-packages/distro/__pycache__/distro.cpython-311.pyc
ADDED
|
Binary file (57.8 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/distro/distro.py
ADDED
|
@@ -0,0 +1,1403 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python
|
| 2 |
+
# Copyright 2015-2021 Nir Cohen
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
|
| 16 |
+
"""
|
| 17 |
+
The ``distro`` package (``distro`` stands for Linux Distribution) provides
|
| 18 |
+
information about the Linux distribution it runs on, such as a reliable
|
| 19 |
+
machine-readable distro ID, or version information.
|
| 20 |
+
|
| 21 |
+
It is the recommended replacement for Python's original
|
| 22 |
+
:py:func:`platform.linux_distribution` function, but it provides much more
|
| 23 |
+
functionality. An alternative implementation became necessary because Python
|
| 24 |
+
3.5 deprecated this function, and Python 3.8 removed it altogether. Its
|
| 25 |
+
predecessor function :py:func:`platform.dist` was already deprecated since
|
| 26 |
+
Python 2.6 and removed in Python 3.8. Still, there are many cases in which
|
| 27 |
+
access to OS distribution information is needed. See `Python issue 1322
|
| 28 |
+
<https://bugs.python.org/issue1322>`_ for more information.
|
| 29 |
+
"""
|
| 30 |
+
|
| 31 |
+
import argparse
|
| 32 |
+
import json
|
| 33 |
+
import logging
|
| 34 |
+
import os
|
| 35 |
+
import re
|
| 36 |
+
import shlex
|
| 37 |
+
import subprocess
|
| 38 |
+
import sys
|
| 39 |
+
import warnings
|
| 40 |
+
from typing import (
|
| 41 |
+
Any,
|
| 42 |
+
Callable,
|
| 43 |
+
Dict,
|
| 44 |
+
Iterable,
|
| 45 |
+
Optional,
|
| 46 |
+
Sequence,
|
| 47 |
+
TextIO,
|
| 48 |
+
Tuple,
|
| 49 |
+
Type,
|
| 50 |
+
)
|
| 51 |
+
|
| 52 |
+
try:
|
| 53 |
+
from typing import TypedDict
|
| 54 |
+
except ImportError:
|
| 55 |
+
# Python 3.7
|
| 56 |
+
TypedDict = dict
|
| 57 |
+
|
| 58 |
+
__version__ = "1.9.0"
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
class VersionDict(TypedDict):
|
| 62 |
+
major: str
|
| 63 |
+
minor: str
|
| 64 |
+
build_number: str
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
class InfoDict(TypedDict):
|
| 68 |
+
id: str
|
| 69 |
+
version: str
|
| 70 |
+
version_parts: VersionDict
|
| 71 |
+
like: str
|
| 72 |
+
codename: str
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
_UNIXCONFDIR = os.environ.get("UNIXCONFDIR", "/etc")
|
| 76 |
+
_UNIXUSRLIBDIR = os.environ.get("UNIXUSRLIBDIR", "/usr/lib")
|
| 77 |
+
_OS_RELEASE_BASENAME = "os-release"
|
| 78 |
+
|
| 79 |
+
#: Translation table for normalizing the "ID" attribute defined in os-release
|
| 80 |
+
#: files, for use by the :func:`distro.id` method.
|
| 81 |
+
#:
|
| 82 |
+
#: * Key: Value as defined in the os-release file, translated to lower case,
|
| 83 |
+
#: with blanks translated to underscores.
|
| 84 |
+
#:
|
| 85 |
+
#: * Value: Normalized value.
|
| 86 |
+
NORMALIZED_OS_ID = {
|
| 87 |
+
"ol": "oracle", # Oracle Linux
|
| 88 |
+
"opensuse-leap": "opensuse", # Newer versions of OpenSuSE report as opensuse-leap
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
#: Translation table for normalizing the "Distributor ID" attribute returned by
|
| 92 |
+
#: the lsb_release command, for use by the :func:`distro.id` method.
|
| 93 |
+
#:
|
| 94 |
+
#: * Key: Value as returned by the lsb_release command, translated to lower
|
| 95 |
+
#: case, with blanks translated to underscores.
|
| 96 |
+
#:
|
| 97 |
+
#: * Value: Normalized value.
|
| 98 |
+
NORMALIZED_LSB_ID = {
|
| 99 |
+
"enterpriseenterpriseas": "oracle", # Oracle Enterprise Linux 4
|
| 100 |
+
"enterpriseenterpriseserver": "oracle", # Oracle Linux 5
|
| 101 |
+
"redhatenterpriseworkstation": "rhel", # RHEL 6, 7 Workstation
|
| 102 |
+
"redhatenterpriseserver": "rhel", # RHEL 6, 7 Server
|
| 103 |
+
"redhatenterprisecomputenode": "rhel", # RHEL 6 ComputeNode
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
#: Translation table for normalizing the distro ID derived from the file name
|
| 107 |
+
#: of distro release files, for use by the :func:`distro.id` method.
|
| 108 |
+
#:
|
| 109 |
+
#: * Key: Value as derived from the file name of a distro release file,
|
| 110 |
+
#: translated to lower case, with blanks translated to underscores.
|
| 111 |
+
#:
|
| 112 |
+
#: * Value: Normalized value.
|
| 113 |
+
NORMALIZED_DISTRO_ID = {
|
| 114 |
+
"redhat": "rhel", # RHEL 6.x, 7.x
|
| 115 |
+
}
|
| 116 |
+
|
| 117 |
+
# Pattern for content of distro release file (reversed)
|
| 118 |
+
_DISTRO_RELEASE_CONTENT_REVERSED_PATTERN = re.compile(
|
| 119 |
+
r"(?:[^)]*\)(.*)\()? *(?:STL )?([\d.+\-a-z]*\d) *(?:esaeler *)?(.+)"
|
| 120 |
+
)
|
| 121 |
+
|
| 122 |
+
# Pattern for base file name of distro release file
|
| 123 |
+
_DISTRO_RELEASE_BASENAME_PATTERN = re.compile(r"(\w+)[-_](release|version)$")
|
| 124 |
+
|
| 125 |
+
# Base file names to be looked up for if _UNIXCONFDIR is not readable.
|
| 126 |
+
_DISTRO_RELEASE_BASENAMES = [
|
| 127 |
+
"SuSE-release",
|
| 128 |
+
"altlinux-release",
|
| 129 |
+
"arch-release",
|
| 130 |
+
"base-release",
|
| 131 |
+
"centos-release",
|
| 132 |
+
"fedora-release",
|
| 133 |
+
"gentoo-release",
|
| 134 |
+
"mageia-release",
|
| 135 |
+
"mandrake-release",
|
| 136 |
+
"mandriva-release",
|
| 137 |
+
"mandrivalinux-release",
|
| 138 |
+
"manjaro-release",
|
| 139 |
+
"oracle-release",
|
| 140 |
+
"redhat-release",
|
| 141 |
+
"rocky-release",
|
| 142 |
+
"sl-release",
|
| 143 |
+
"slackware-version",
|
| 144 |
+
]
|
| 145 |
+
|
| 146 |
+
# Base file names to be ignored when searching for distro release file
|
| 147 |
+
_DISTRO_RELEASE_IGNORE_BASENAMES = (
|
| 148 |
+
"debian_version",
|
| 149 |
+
"lsb-release",
|
| 150 |
+
"oem-release",
|
| 151 |
+
_OS_RELEASE_BASENAME,
|
| 152 |
+
"system-release",
|
| 153 |
+
"plesk-release",
|
| 154 |
+
"iredmail-release",
|
| 155 |
+
"board-release",
|
| 156 |
+
"ec2_version",
|
| 157 |
+
)
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
def linux_distribution(full_distribution_name: bool = True) -> Tuple[str, str, str]:
|
| 161 |
+
"""
|
| 162 |
+
.. deprecated:: 1.6.0
|
| 163 |
+
|
| 164 |
+
:func:`distro.linux_distribution()` is deprecated. It should only be
|
| 165 |
+
used as a compatibility shim with Python's
|
| 166 |
+
:py:func:`platform.linux_distribution()`. Please use :func:`distro.id`,
|
| 167 |
+
:func:`distro.version` and :func:`distro.name` instead.
|
| 168 |
+
|
| 169 |
+
Return information about the current OS distribution as a tuple
|
| 170 |
+
``(id_name, version, codename)`` with items as follows:
|
| 171 |
+
|
| 172 |
+
* ``id_name``: If *full_distribution_name* is false, the result of
|
| 173 |
+
:func:`distro.id`. Otherwise, the result of :func:`distro.name`.
|
| 174 |
+
|
| 175 |
+
* ``version``: The result of :func:`distro.version`.
|
| 176 |
+
|
| 177 |
+
* ``codename``: The extra item (usually in parentheses) after the
|
| 178 |
+
os-release version number, or the result of :func:`distro.codename`.
|
| 179 |
+
|
| 180 |
+
The interface of this function is compatible with the original
|
| 181 |
+
:py:func:`platform.linux_distribution` function, supporting a subset of
|
| 182 |
+
its parameters.
|
| 183 |
+
|
| 184 |
+
The data it returns may not exactly be the same, because it uses more data
|
| 185 |
+
sources than the original function, and that may lead to different data if
|
| 186 |
+
the OS distribution is not consistent across multiple data sources it
|
| 187 |
+
provides (there are indeed such distributions ...).
|
| 188 |
+
|
| 189 |
+
Another reason for differences is the fact that the :func:`distro.id`
|
| 190 |
+
method normalizes the distro ID string to a reliable machine-readable value
|
| 191 |
+
for a number of popular OS distributions.
|
| 192 |
+
"""
|
| 193 |
+
warnings.warn(
|
| 194 |
+
"distro.linux_distribution() is deprecated. It should only be used as a "
|
| 195 |
+
"compatibility shim with Python's platform.linux_distribution(). Please use "
|
| 196 |
+
"distro.id(), distro.version() and distro.name() instead.",
|
| 197 |
+
DeprecationWarning,
|
| 198 |
+
stacklevel=2,
|
| 199 |
+
)
|
| 200 |
+
return _distro.linux_distribution(full_distribution_name)
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
def id() -> str:
|
| 204 |
+
"""
|
| 205 |
+
Return the distro ID of the current distribution, as a
|
| 206 |
+
machine-readable string.
|
| 207 |
+
|
| 208 |
+
For a number of OS distributions, the returned distro ID value is
|
| 209 |
+
*reliable*, in the sense that it is documented and that it does not change
|
| 210 |
+
across releases of the distribution.
|
| 211 |
+
|
| 212 |
+
This package maintains the following reliable distro ID values:
|
| 213 |
+
|
| 214 |
+
============== =========================================
|
| 215 |
+
Distro ID Distribution
|
| 216 |
+
============== =========================================
|
| 217 |
+
"ubuntu" Ubuntu
|
| 218 |
+
"debian" Debian
|
| 219 |
+
"rhel" RedHat Enterprise Linux
|
| 220 |
+
"centos" CentOS
|
| 221 |
+
"fedora" Fedora
|
| 222 |
+
"sles" SUSE Linux Enterprise Server
|
| 223 |
+
"opensuse" openSUSE
|
| 224 |
+
"amzn" Amazon Linux
|
| 225 |
+
"arch" Arch Linux
|
| 226 |
+
"buildroot" Buildroot
|
| 227 |
+
"cloudlinux" CloudLinux OS
|
| 228 |
+
"exherbo" Exherbo Linux
|
| 229 |
+
"gentoo" GenToo Linux
|
| 230 |
+
"ibm_powerkvm" IBM PowerKVM
|
| 231 |
+
"kvmibm" KVM for IBM z Systems
|
| 232 |
+
"linuxmint" Linux Mint
|
| 233 |
+
"mageia" Mageia
|
| 234 |
+
"mandriva" Mandriva Linux
|
| 235 |
+
"parallels" Parallels
|
| 236 |
+
"pidora" Pidora
|
| 237 |
+
"raspbian" Raspbian
|
| 238 |
+
"oracle" Oracle Linux (and Oracle Enterprise Linux)
|
| 239 |
+
"scientific" Scientific Linux
|
| 240 |
+
"slackware" Slackware
|
| 241 |
+
"xenserver" XenServer
|
| 242 |
+
"openbsd" OpenBSD
|
| 243 |
+
"netbsd" NetBSD
|
| 244 |
+
"freebsd" FreeBSD
|
| 245 |
+
"midnightbsd" MidnightBSD
|
| 246 |
+
"rocky" Rocky Linux
|
| 247 |
+
"aix" AIX
|
| 248 |
+
"guix" Guix System
|
| 249 |
+
"altlinux" ALT Linux
|
| 250 |
+
============== =========================================
|
| 251 |
+
|
| 252 |
+
If you have a need to get distros for reliable IDs added into this set,
|
| 253 |
+
or if you find that the :func:`distro.id` function returns a different
|
| 254 |
+
distro ID for one of the listed distros, please create an issue in the
|
| 255 |
+
`distro issue tracker`_.
|
| 256 |
+
|
| 257 |
+
**Lookup hierarchy and transformations:**
|
| 258 |
+
|
| 259 |
+
First, the ID is obtained from the following sources, in the specified
|
| 260 |
+
order. The first available and non-empty value is used:
|
| 261 |
+
|
| 262 |
+
* the value of the "ID" attribute of the os-release file,
|
| 263 |
+
|
| 264 |
+
* the value of the "Distributor ID" attribute returned by the lsb_release
|
| 265 |
+
command,
|
| 266 |
+
|
| 267 |
+
* the first part of the file name of the distro release file,
|
| 268 |
+
|
| 269 |
+
The so determined ID value then passes the following transformations,
|
| 270 |
+
before it is returned by this method:
|
| 271 |
+
|
| 272 |
+
* it is translated to lower case,
|
| 273 |
+
|
| 274 |
+
* blanks (which should not be there anyway) are translated to underscores,
|
| 275 |
+
|
| 276 |
+
* a normalization of the ID is performed, based upon
|
| 277 |
+
`normalization tables`_. The purpose of this normalization is to ensure
|
| 278 |
+
that the ID is as reliable as possible, even across incompatible changes
|
| 279 |
+
in the OS distributions. A common reason for an incompatible change is
|
| 280 |
+
the addition of an os-release file, or the addition of the lsb_release
|
| 281 |
+
command, with ID values that differ from what was previously determined
|
| 282 |
+
from the distro release file name.
|
| 283 |
+
"""
|
| 284 |
+
return _distro.id()
|
| 285 |
+
|
| 286 |
+
|
| 287 |
+
def name(pretty: bool = False) -> str:
|
| 288 |
+
"""
|
| 289 |
+
Return the name of the current OS distribution, as a human-readable
|
| 290 |
+
string.
|
| 291 |
+
|
| 292 |
+
If *pretty* is false, the name is returned without version or codename.
|
| 293 |
+
(e.g. "CentOS Linux")
|
| 294 |
+
|
| 295 |
+
If *pretty* is true, the version and codename are appended.
|
| 296 |
+
(e.g. "CentOS Linux 7.1.1503 (Core)")
|
| 297 |
+
|
| 298 |
+
**Lookup hierarchy:**
|
| 299 |
+
|
| 300 |
+
The name is obtained from the following sources, in the specified order.
|
| 301 |
+
The first available and non-empty value is used:
|
| 302 |
+
|
| 303 |
+
* If *pretty* is false:
|
| 304 |
+
|
| 305 |
+
- the value of the "NAME" attribute of the os-release file,
|
| 306 |
+
|
| 307 |
+
- the value of the "Distributor ID" attribute returned by the lsb_release
|
| 308 |
+
command,
|
| 309 |
+
|
| 310 |
+
- the value of the "<name>" field of the distro release file.
|
| 311 |
+
|
| 312 |
+
* If *pretty* is true:
|
| 313 |
+
|
| 314 |
+
- the value of the "PRETTY_NAME" attribute of the os-release file,
|
| 315 |
+
|
| 316 |
+
- the value of the "Description" attribute returned by the lsb_release
|
| 317 |
+
command,
|
| 318 |
+
|
| 319 |
+
- the value of the "<name>" field of the distro release file, appended
|
| 320 |
+
with the value of the pretty version ("<version_id>" and "<codename>"
|
| 321 |
+
fields) of the distro release file, if available.
|
| 322 |
+
"""
|
| 323 |
+
return _distro.name(pretty)
|
| 324 |
+
|
| 325 |
+
|
| 326 |
+
def version(pretty: bool = False, best: bool = False) -> str:
|
| 327 |
+
"""
|
| 328 |
+
Return the version of the current OS distribution, as a human-readable
|
| 329 |
+
string.
|
| 330 |
+
|
| 331 |
+
If *pretty* is false, the version is returned without codename (e.g.
|
| 332 |
+
"7.0").
|
| 333 |
+
|
| 334 |
+
If *pretty* is true, the codename in parenthesis is appended, if the
|
| 335 |
+
codename is non-empty (e.g. "7.0 (Maipo)").
|
| 336 |
+
|
| 337 |
+
Some distributions provide version numbers with different precisions in
|
| 338 |
+
the different sources of distribution information. Examining the different
|
| 339 |
+
sources in a fixed priority order does not always yield the most precise
|
| 340 |
+
version (e.g. for Debian 8.2, or CentOS 7.1).
|
| 341 |
+
|
| 342 |
+
Some other distributions may not provide this kind of information. In these
|
| 343 |
+
cases, an empty string would be returned. This behavior can be observed
|
| 344 |
+
with rolling releases distributions (e.g. Arch Linux).
|
| 345 |
+
|
| 346 |
+
The *best* parameter can be used to control the approach for the returned
|
| 347 |
+
version:
|
| 348 |
+
|
| 349 |
+
If *best* is false, the first non-empty version number in priority order of
|
| 350 |
+
the examined sources is returned.
|
| 351 |
+
|
| 352 |
+
If *best* is true, the most precise version number out of all examined
|
| 353 |
+
sources is returned.
|
| 354 |
+
|
| 355 |
+
**Lookup hierarchy:**
|
| 356 |
+
|
| 357 |
+
In all cases, the version number is obtained from the following sources.
|
| 358 |
+
If *best* is false, this order represents the priority order:
|
| 359 |
+
|
| 360 |
+
* the value of the "VERSION_ID" attribute of the os-release file,
|
| 361 |
+
* the value of the "Release" attribute returned by the lsb_release
|
| 362 |
+
command,
|
| 363 |
+
* the version number parsed from the "<version_id>" field of the first line
|
| 364 |
+
of the distro release file,
|
| 365 |
+
* the version number parsed from the "PRETTY_NAME" attribute of the
|
| 366 |
+
os-release file, if it follows the format of the distro release files.
|
| 367 |
+
* the version number parsed from the "Description" attribute returned by
|
| 368 |
+
the lsb_release command, if it follows the format of the distro release
|
| 369 |
+
files.
|
| 370 |
+
"""
|
| 371 |
+
return _distro.version(pretty, best)
|
| 372 |
+
|
| 373 |
+
|
| 374 |
+
def version_parts(best: bool = False) -> Tuple[str, str, str]:
|
| 375 |
+
"""
|
| 376 |
+
Return the version of the current OS distribution as a tuple
|
| 377 |
+
``(major, minor, build_number)`` with items as follows:
|
| 378 |
+
|
| 379 |
+
* ``major``: The result of :func:`distro.major_version`.
|
| 380 |
+
|
| 381 |
+
* ``minor``: The result of :func:`distro.minor_version`.
|
| 382 |
+
|
| 383 |
+
* ``build_number``: The result of :func:`distro.build_number`.
|
| 384 |
+
|
| 385 |
+
For a description of the *best* parameter, see the :func:`distro.version`
|
| 386 |
+
method.
|
| 387 |
+
"""
|
| 388 |
+
return _distro.version_parts(best)
|
| 389 |
+
|
| 390 |
+
|
| 391 |
+
def major_version(best: bool = False) -> str:
|
| 392 |
+
"""
|
| 393 |
+
Return the major version of the current OS distribution, as a string,
|
| 394 |
+
if provided.
|
| 395 |
+
Otherwise, the empty string is returned. The major version is the first
|
| 396 |
+
part of the dot-separated version string.
|
| 397 |
+
|
| 398 |
+
For a description of the *best* parameter, see the :func:`distro.version`
|
| 399 |
+
method.
|
| 400 |
+
"""
|
| 401 |
+
return _distro.major_version(best)
|
| 402 |
+
|
| 403 |
+
|
| 404 |
+
def minor_version(best: bool = False) -> str:
|
| 405 |
+
"""
|
| 406 |
+
Return the minor version of the current OS distribution, as a string,
|
| 407 |
+
if provided.
|
| 408 |
+
Otherwise, the empty string is returned. The minor version is the second
|
| 409 |
+
part of the dot-separated version string.
|
| 410 |
+
|
| 411 |
+
For a description of the *best* parameter, see the :func:`distro.version`
|
| 412 |
+
method.
|
| 413 |
+
"""
|
| 414 |
+
return _distro.minor_version(best)
|
| 415 |
+
|
| 416 |
+
|
| 417 |
+
def build_number(best: bool = False) -> str:
|
| 418 |
+
"""
|
| 419 |
+
Return the build number of the current OS distribution, as a string,
|
| 420 |
+
if provided.
|
| 421 |
+
Otherwise, the empty string is returned. The build number is the third part
|
| 422 |
+
of the dot-separated version string.
|
| 423 |
+
|
| 424 |
+
For a description of the *best* parameter, see the :func:`distro.version`
|
| 425 |
+
method.
|
| 426 |
+
"""
|
| 427 |
+
return _distro.build_number(best)
|
| 428 |
+
|
| 429 |
+
|
| 430 |
+
def like() -> str:
|
| 431 |
+
"""
|
| 432 |
+
Return a space-separated list of distro IDs of distributions that are
|
| 433 |
+
closely related to the current OS distribution in regards to packaging
|
| 434 |
+
and programming interfaces, for example distributions the current
|
| 435 |
+
distribution is a derivative from.
|
| 436 |
+
|
| 437 |
+
**Lookup hierarchy:**
|
| 438 |
+
|
| 439 |
+
This information item is only provided by the os-release file.
|
| 440 |
+
For details, see the description of the "ID_LIKE" attribute in the
|
| 441 |
+
`os-release man page
|
| 442 |
+
<http://www.freedesktop.org/software/systemd/man/os-release.html>`_.
|
| 443 |
+
"""
|
| 444 |
+
return _distro.like()
|
| 445 |
+
|
| 446 |
+
|
| 447 |
+
def codename() -> str:
|
| 448 |
+
"""
|
| 449 |
+
Return the codename for the release of the current OS distribution,
|
| 450 |
+
as a string.
|
| 451 |
+
|
| 452 |
+
If the distribution does not have a codename, an empty string is returned.
|
| 453 |
+
|
| 454 |
+
Note that the returned codename is not always really a codename. For
|
| 455 |
+
example, openSUSE returns "x86_64". This function does not handle such
|
| 456 |
+
cases in any special way and just returns the string it finds, if any.
|
| 457 |
+
|
| 458 |
+
**Lookup hierarchy:**
|
| 459 |
+
|
| 460 |
+
* the codename within the "VERSION" attribute of the os-release file, if
|
| 461 |
+
provided,
|
| 462 |
+
|
| 463 |
+
* the value of the "Codename" attribute returned by the lsb_release
|
| 464 |
+
command,
|
| 465 |
+
|
| 466 |
+
* the value of the "<codename>" field of the distro release file.
|
| 467 |
+
"""
|
| 468 |
+
return _distro.codename()
|
| 469 |
+
|
| 470 |
+
|
| 471 |
+
def info(pretty: bool = False, best: bool = False) -> InfoDict:
|
| 472 |
+
"""
|
| 473 |
+
Return certain machine-readable information items about the current OS
|
| 474 |
+
distribution in a dictionary, as shown in the following example:
|
| 475 |
+
|
| 476 |
+
.. sourcecode:: python
|
| 477 |
+
|
| 478 |
+
{
|
| 479 |
+
'id': 'rhel',
|
| 480 |
+
'version': '7.0',
|
| 481 |
+
'version_parts': {
|
| 482 |
+
'major': '7',
|
| 483 |
+
'minor': '0',
|
| 484 |
+
'build_number': ''
|
| 485 |
+
},
|
| 486 |
+
'like': 'fedora',
|
| 487 |
+
'codename': 'Maipo'
|
| 488 |
+
}
|
| 489 |
+
|
| 490 |
+
The dictionary structure and keys are always the same, regardless of which
|
| 491 |
+
information items are available in the underlying data sources. The values
|
| 492 |
+
for the various keys are as follows:
|
| 493 |
+
|
| 494 |
+
* ``id``: The result of :func:`distro.id`.
|
| 495 |
+
|
| 496 |
+
* ``version``: The result of :func:`distro.version`.
|
| 497 |
+
|
| 498 |
+
* ``version_parts -> major``: The result of :func:`distro.major_version`.
|
| 499 |
+
|
| 500 |
+
* ``version_parts -> minor``: The result of :func:`distro.minor_version`.
|
| 501 |
+
|
| 502 |
+
* ``version_parts -> build_number``: The result of
|
| 503 |
+
:func:`distro.build_number`.
|
| 504 |
+
|
| 505 |
+
* ``like``: The result of :func:`distro.like`.
|
| 506 |
+
|
| 507 |
+
* ``codename``: The result of :func:`distro.codename`.
|
| 508 |
+
|
| 509 |
+
For a description of the *pretty* and *best* parameters, see the
|
| 510 |
+
:func:`distro.version` method.
|
| 511 |
+
"""
|
| 512 |
+
return _distro.info(pretty, best)
|
| 513 |
+
|
| 514 |
+
|
| 515 |
+
def os_release_info() -> Dict[str, str]:
|
| 516 |
+
"""
|
| 517 |
+
Return a dictionary containing key-value pairs for the information items
|
| 518 |
+
from the os-release file data source of the current OS distribution.
|
| 519 |
+
|
| 520 |
+
See `os-release file`_ for details about these information items.
|
| 521 |
+
"""
|
| 522 |
+
return _distro.os_release_info()
|
| 523 |
+
|
| 524 |
+
|
| 525 |
+
def lsb_release_info() -> Dict[str, str]:
|
| 526 |
+
"""
|
| 527 |
+
Return a dictionary containing key-value pairs for the information items
|
| 528 |
+
from the lsb_release command data source of the current OS distribution.
|
| 529 |
+
|
| 530 |
+
See `lsb_release command output`_ for details about these information
|
| 531 |
+
items.
|
| 532 |
+
"""
|
| 533 |
+
return _distro.lsb_release_info()
|
| 534 |
+
|
| 535 |
+
|
| 536 |
+
def distro_release_info() -> Dict[str, str]:
|
| 537 |
+
"""
|
| 538 |
+
Return a dictionary containing key-value pairs for the information items
|
| 539 |
+
from the distro release file data source of the current OS distribution.
|
| 540 |
+
|
| 541 |
+
See `distro release file`_ for details about these information items.
|
| 542 |
+
"""
|
| 543 |
+
return _distro.distro_release_info()
|
| 544 |
+
|
| 545 |
+
|
| 546 |
+
def uname_info() -> Dict[str, str]:
|
| 547 |
+
"""
|
| 548 |
+
Return a dictionary containing key-value pairs for the information items
|
| 549 |
+
from the distro release file data source of the current OS distribution.
|
| 550 |
+
"""
|
| 551 |
+
return _distro.uname_info()
|
| 552 |
+
|
| 553 |
+
|
| 554 |
+
def os_release_attr(attribute: str) -> str:
|
| 555 |
+
"""
|
| 556 |
+
Return a single named information item from the os-release file data source
|
| 557 |
+
of the current OS distribution.
|
| 558 |
+
|
| 559 |
+
Parameters:
|
| 560 |
+
|
| 561 |
+
* ``attribute`` (string): Key of the information item.
|
| 562 |
+
|
| 563 |
+
Returns:
|
| 564 |
+
|
| 565 |
+
* (string): Value of the information item, if the item exists.
|
| 566 |
+
The empty string, if the item does not exist.
|
| 567 |
+
|
| 568 |
+
See `os-release file`_ for details about these information items.
|
| 569 |
+
"""
|
| 570 |
+
return _distro.os_release_attr(attribute)
|
| 571 |
+
|
| 572 |
+
|
| 573 |
+
def lsb_release_attr(attribute: str) -> str:
|
| 574 |
+
"""
|
| 575 |
+
Return a single named information item from the lsb_release command output
|
| 576 |
+
data source of the current OS distribution.
|
| 577 |
+
|
| 578 |
+
Parameters:
|
| 579 |
+
|
| 580 |
+
* ``attribute`` (string): Key of the information item.
|
| 581 |
+
|
| 582 |
+
Returns:
|
| 583 |
+
|
| 584 |
+
* (string): Value of the information item, if the item exists.
|
| 585 |
+
The empty string, if the item does not exist.
|
| 586 |
+
|
| 587 |
+
See `lsb_release command output`_ for details about these information
|
| 588 |
+
items.
|
| 589 |
+
"""
|
| 590 |
+
return _distro.lsb_release_attr(attribute)
|
| 591 |
+
|
| 592 |
+
|
| 593 |
+
def distro_release_attr(attribute: str) -> str:
|
| 594 |
+
"""
|
| 595 |
+
Return a single named information item from the distro release file
|
| 596 |
+
data source of the current OS distribution.
|
| 597 |
+
|
| 598 |
+
Parameters:
|
| 599 |
+
|
| 600 |
+
* ``attribute`` (string): Key of the information item.
|
| 601 |
+
|
| 602 |
+
Returns:
|
| 603 |
+
|
| 604 |
+
* (string): Value of the information item, if the item exists.
|
| 605 |
+
The empty string, if the item does not exist.
|
| 606 |
+
|
| 607 |
+
See `distro release file`_ for details about these information items.
|
| 608 |
+
"""
|
| 609 |
+
return _distro.distro_release_attr(attribute)
|
| 610 |
+
|
| 611 |
+
|
| 612 |
+
def uname_attr(attribute: str) -> str:
|
| 613 |
+
"""
|
| 614 |
+
Return a single named information item from the distro release file
|
| 615 |
+
data source of the current OS distribution.
|
| 616 |
+
|
| 617 |
+
Parameters:
|
| 618 |
+
|
| 619 |
+
* ``attribute`` (string): Key of the information item.
|
| 620 |
+
|
| 621 |
+
Returns:
|
| 622 |
+
|
| 623 |
+
* (string): Value of the information item, if the item exists.
|
| 624 |
+
The empty string, if the item does not exist.
|
| 625 |
+
"""
|
| 626 |
+
return _distro.uname_attr(attribute)
|
| 627 |
+
|
| 628 |
+
|
| 629 |
+
try:
|
| 630 |
+
from functools import cached_property
|
| 631 |
+
except ImportError:
|
| 632 |
+
# Python < 3.8
|
| 633 |
+
class cached_property: # type: ignore
|
| 634 |
+
"""A version of @property which caches the value. On access, it calls the
|
| 635 |
+
underlying function and sets the value in `__dict__` so future accesses
|
| 636 |
+
will not re-call the property.
|
| 637 |
+
"""
|
| 638 |
+
|
| 639 |
+
def __init__(self, f: Callable[[Any], Any]) -> None:
|
| 640 |
+
self._fname = f.__name__
|
| 641 |
+
self._f = f
|
| 642 |
+
|
| 643 |
+
def __get__(self, obj: Any, owner: Type[Any]) -> Any:
|
| 644 |
+
assert obj is not None, f"call {self._fname} on an instance"
|
| 645 |
+
ret = obj.__dict__[self._fname] = self._f(obj)
|
| 646 |
+
return ret
|
| 647 |
+
|
| 648 |
+
|
| 649 |
+
class LinuxDistribution:
|
| 650 |
+
"""
|
| 651 |
+
Provides information about a OS distribution.
|
| 652 |
+
|
| 653 |
+
This package creates a private module-global instance of this class with
|
| 654 |
+
default initialization arguments, that is used by the
|
| 655 |
+
`consolidated accessor functions`_ and `single source accessor functions`_.
|
| 656 |
+
By using default initialization arguments, that module-global instance
|
| 657 |
+
returns data about the current OS distribution (i.e. the distro this
|
| 658 |
+
package runs on).
|
| 659 |
+
|
| 660 |
+
Normally, it is not necessary to create additional instances of this class.
|
| 661 |
+
However, in situations where control is needed over the exact data sources
|
| 662 |
+
that are used, instances of this class can be created with a specific
|
| 663 |
+
distro release file, or a specific os-release file, or without invoking the
|
| 664 |
+
lsb_release command.
|
| 665 |
+
"""
|
| 666 |
+
|
| 667 |
+
def __init__(
|
| 668 |
+
self,
|
| 669 |
+
include_lsb: Optional[bool] = None,
|
| 670 |
+
os_release_file: str = "",
|
| 671 |
+
distro_release_file: str = "",
|
| 672 |
+
include_uname: Optional[bool] = None,
|
| 673 |
+
root_dir: Optional[str] = None,
|
| 674 |
+
include_oslevel: Optional[bool] = None,
|
| 675 |
+
) -> None:
|
| 676 |
+
"""
|
| 677 |
+
The initialization method of this class gathers information from the
|
| 678 |
+
available data sources, and stores that in private instance attributes.
|
| 679 |
+
Subsequent access to the information items uses these private instance
|
| 680 |
+
attributes, so that the data sources are read only once.
|
| 681 |
+
|
| 682 |
+
Parameters:
|
| 683 |
+
|
| 684 |
+
* ``include_lsb`` (bool): Controls whether the
|
| 685 |
+
`lsb_release command output`_ is included as a data source.
|
| 686 |
+
|
| 687 |
+
If the lsb_release command is not available in the program execution
|
| 688 |
+
path, the data source for the lsb_release command will be empty.
|
| 689 |
+
|
| 690 |
+
* ``os_release_file`` (string): The path name of the
|
| 691 |
+
`os-release file`_ that is to be used as a data source.
|
| 692 |
+
|
| 693 |
+
An empty string (the default) will cause the default path name to
|
| 694 |
+
be used (see `os-release file`_ for details).
|
| 695 |
+
|
| 696 |
+
If the specified or defaulted os-release file does not exist, the
|
| 697 |
+
data source for the os-release file will be empty.
|
| 698 |
+
|
| 699 |
+
* ``distro_release_file`` (string): The path name of the
|
| 700 |
+
`distro release file`_ that is to be used as a data source.
|
| 701 |
+
|
| 702 |
+
An empty string (the default) will cause a default search algorithm
|
| 703 |
+
to be used (see `distro release file`_ for details).
|
| 704 |
+
|
| 705 |
+
If the specified distro release file does not exist, or if no default
|
| 706 |
+
distro release file can be found, the data source for the distro
|
| 707 |
+
release file will be empty.
|
| 708 |
+
|
| 709 |
+
* ``include_uname`` (bool): Controls whether uname command output is
|
| 710 |
+
included as a data source. If the uname command is not available in
|
| 711 |
+
the program execution path the data source for the uname command will
|
| 712 |
+
be empty.
|
| 713 |
+
|
| 714 |
+
* ``root_dir`` (string): The absolute path to the root directory to use
|
| 715 |
+
to find distro-related information files. Note that ``include_*``
|
| 716 |
+
parameters must not be enabled in combination with ``root_dir``.
|
| 717 |
+
|
| 718 |
+
* ``include_oslevel`` (bool): Controls whether (AIX) oslevel command
|
| 719 |
+
output is included as a data source. If the oslevel command is not
|
| 720 |
+
available in the program execution path the data source will be
|
| 721 |
+
empty.
|
| 722 |
+
|
| 723 |
+
Public instance attributes:
|
| 724 |
+
|
| 725 |
+
* ``os_release_file`` (string): The path name of the
|
| 726 |
+
`os-release file`_ that is actually used as a data source. The
|
| 727 |
+
empty string if no distro release file is used as a data source.
|
| 728 |
+
|
| 729 |
+
* ``distro_release_file`` (string): The path name of the
|
| 730 |
+
`distro release file`_ that is actually used as a data source. The
|
| 731 |
+
empty string if no distro release file is used as a data source.
|
| 732 |
+
|
| 733 |
+
* ``include_lsb`` (bool): The result of the ``include_lsb`` parameter.
|
| 734 |
+
This controls whether the lsb information will be loaded.
|
| 735 |
+
|
| 736 |
+
* ``include_uname`` (bool): The result of the ``include_uname``
|
| 737 |
+
parameter. This controls whether the uname information will
|
| 738 |
+
be loaded.
|
| 739 |
+
|
| 740 |
+
* ``include_oslevel`` (bool): The result of the ``include_oslevel``
|
| 741 |
+
parameter. This controls whether (AIX) oslevel information will be
|
| 742 |
+
loaded.
|
| 743 |
+
|
| 744 |
+
* ``root_dir`` (string): The result of the ``root_dir`` parameter.
|
| 745 |
+
The absolute path to the root directory to use to find distro-related
|
| 746 |
+
information files.
|
| 747 |
+
|
| 748 |
+
Raises:
|
| 749 |
+
|
| 750 |
+
* :py:exc:`ValueError`: Initialization parameters combination is not
|
| 751 |
+
supported.
|
| 752 |
+
|
| 753 |
+
* :py:exc:`OSError`: Some I/O issue with an os-release file or distro
|
| 754 |
+
release file.
|
| 755 |
+
|
| 756 |
+
* :py:exc:`UnicodeError`: A data source has unexpected characters or
|
| 757 |
+
uses an unexpected encoding.
|
| 758 |
+
"""
|
| 759 |
+
self.root_dir = root_dir
|
| 760 |
+
self.etc_dir = os.path.join(root_dir, "etc") if root_dir else _UNIXCONFDIR
|
| 761 |
+
self.usr_lib_dir = (
|
| 762 |
+
os.path.join(root_dir, "usr/lib") if root_dir else _UNIXUSRLIBDIR
|
| 763 |
+
)
|
| 764 |
+
|
| 765 |
+
if os_release_file:
|
| 766 |
+
self.os_release_file = os_release_file
|
| 767 |
+
else:
|
| 768 |
+
etc_dir_os_release_file = os.path.join(self.etc_dir, _OS_RELEASE_BASENAME)
|
| 769 |
+
usr_lib_os_release_file = os.path.join(
|
| 770 |
+
self.usr_lib_dir, _OS_RELEASE_BASENAME
|
| 771 |
+
)
|
| 772 |
+
|
| 773 |
+
# NOTE: The idea is to respect order **and** have it set
|
| 774 |
+
# at all times for API backwards compatibility.
|
| 775 |
+
if os.path.isfile(etc_dir_os_release_file) or not os.path.isfile(
|
| 776 |
+
usr_lib_os_release_file
|
| 777 |
+
):
|
| 778 |
+
self.os_release_file = etc_dir_os_release_file
|
| 779 |
+
else:
|
| 780 |
+
self.os_release_file = usr_lib_os_release_file
|
| 781 |
+
|
| 782 |
+
self.distro_release_file = distro_release_file or "" # updated later
|
| 783 |
+
|
| 784 |
+
is_root_dir_defined = root_dir is not None
|
| 785 |
+
if is_root_dir_defined and (include_lsb or include_uname or include_oslevel):
|
| 786 |
+
raise ValueError(
|
| 787 |
+
"Including subprocess data sources from specific root_dir is disallowed"
|
| 788 |
+
" to prevent false information"
|
| 789 |
+
)
|
| 790 |
+
self.include_lsb = (
|
| 791 |
+
include_lsb if include_lsb is not None else not is_root_dir_defined
|
| 792 |
+
)
|
| 793 |
+
self.include_uname = (
|
| 794 |
+
include_uname if include_uname is not None else not is_root_dir_defined
|
| 795 |
+
)
|
| 796 |
+
self.include_oslevel = (
|
| 797 |
+
include_oslevel if include_oslevel is not None else not is_root_dir_defined
|
| 798 |
+
)
|
| 799 |
+
|
| 800 |
+
def __repr__(self) -> str:
|
| 801 |
+
"""Return repr of all info"""
|
| 802 |
+
return (
|
| 803 |
+
"LinuxDistribution("
|
| 804 |
+
"os_release_file={self.os_release_file!r}, "
|
| 805 |
+
"distro_release_file={self.distro_release_file!r}, "
|
| 806 |
+
"include_lsb={self.include_lsb!r}, "
|
| 807 |
+
"include_uname={self.include_uname!r}, "
|
| 808 |
+
"include_oslevel={self.include_oslevel!r}, "
|
| 809 |
+
"root_dir={self.root_dir!r}, "
|
| 810 |
+
"_os_release_info={self._os_release_info!r}, "
|
| 811 |
+
"_lsb_release_info={self._lsb_release_info!r}, "
|
| 812 |
+
"_distro_release_info={self._distro_release_info!r}, "
|
| 813 |
+
"_uname_info={self._uname_info!r}, "
|
| 814 |
+
"_oslevel_info={self._oslevel_info!r})".format(self=self)
|
| 815 |
+
)
|
| 816 |
+
|
| 817 |
+
def linux_distribution(
|
| 818 |
+
self, full_distribution_name: bool = True
|
| 819 |
+
) -> Tuple[str, str, str]:
|
| 820 |
+
"""
|
| 821 |
+
Return information about the OS distribution that is compatible
|
| 822 |
+
with Python's :func:`platform.linux_distribution`, supporting a subset
|
| 823 |
+
of its parameters.
|
| 824 |
+
|
| 825 |
+
For details, see :func:`distro.linux_distribution`.
|
| 826 |
+
"""
|
| 827 |
+
return (
|
| 828 |
+
self.name() if full_distribution_name else self.id(),
|
| 829 |
+
self.version(),
|
| 830 |
+
self._os_release_info.get("release_codename") or self.codename(),
|
| 831 |
+
)
|
| 832 |
+
|
| 833 |
+
def id(self) -> str:
|
| 834 |
+
"""Return the distro ID of the OS distribution, as a string.
|
| 835 |
+
|
| 836 |
+
For details, see :func:`distro.id`.
|
| 837 |
+
"""
|
| 838 |
+
|
| 839 |
+
def normalize(distro_id: str, table: Dict[str, str]) -> str:
|
| 840 |
+
distro_id = distro_id.lower().replace(" ", "_")
|
| 841 |
+
return table.get(distro_id, distro_id)
|
| 842 |
+
|
| 843 |
+
distro_id = self.os_release_attr("id")
|
| 844 |
+
if distro_id:
|
| 845 |
+
return normalize(distro_id, NORMALIZED_OS_ID)
|
| 846 |
+
|
| 847 |
+
distro_id = self.lsb_release_attr("distributor_id")
|
| 848 |
+
if distro_id:
|
| 849 |
+
return normalize(distro_id, NORMALIZED_LSB_ID)
|
| 850 |
+
|
| 851 |
+
distro_id = self.distro_release_attr("id")
|
| 852 |
+
if distro_id:
|
| 853 |
+
return normalize(distro_id, NORMALIZED_DISTRO_ID)
|
| 854 |
+
|
| 855 |
+
distro_id = self.uname_attr("id")
|
| 856 |
+
if distro_id:
|
| 857 |
+
return normalize(distro_id, NORMALIZED_DISTRO_ID)
|
| 858 |
+
|
| 859 |
+
return ""
|
| 860 |
+
|
| 861 |
+
def name(self, pretty: bool = False) -> str:
|
| 862 |
+
"""
|
| 863 |
+
Return the name of the OS distribution, as a string.
|
| 864 |
+
|
| 865 |
+
For details, see :func:`distro.name`.
|
| 866 |
+
"""
|
| 867 |
+
name = (
|
| 868 |
+
self.os_release_attr("name")
|
| 869 |
+
or self.lsb_release_attr("distributor_id")
|
| 870 |
+
or self.distro_release_attr("name")
|
| 871 |
+
or self.uname_attr("name")
|
| 872 |
+
)
|
| 873 |
+
if pretty:
|
| 874 |
+
name = self.os_release_attr("pretty_name") or self.lsb_release_attr(
|
| 875 |
+
"description"
|
| 876 |
+
)
|
| 877 |
+
if not name:
|
| 878 |
+
name = self.distro_release_attr("name") or self.uname_attr("name")
|
| 879 |
+
version = self.version(pretty=True)
|
| 880 |
+
if version:
|
| 881 |
+
name = f"{name} {version}"
|
| 882 |
+
return name or ""
|
| 883 |
+
|
| 884 |
+
def version(self, pretty: bool = False, best: bool = False) -> str:
|
| 885 |
+
"""
|
| 886 |
+
Return the version of the OS distribution, as a string.
|
| 887 |
+
|
| 888 |
+
For details, see :func:`distro.version`.
|
| 889 |
+
"""
|
| 890 |
+
versions = [
|
| 891 |
+
self.os_release_attr("version_id"),
|
| 892 |
+
self.lsb_release_attr("release"),
|
| 893 |
+
self.distro_release_attr("version_id"),
|
| 894 |
+
self._parse_distro_release_content(self.os_release_attr("pretty_name")).get(
|
| 895 |
+
"version_id", ""
|
| 896 |
+
),
|
| 897 |
+
self._parse_distro_release_content(
|
| 898 |
+
self.lsb_release_attr("description")
|
| 899 |
+
).get("version_id", ""),
|
| 900 |
+
self.uname_attr("release"),
|
| 901 |
+
]
|
| 902 |
+
if self.uname_attr("id").startswith("aix"):
|
| 903 |
+
# On AIX platforms, prefer oslevel command output.
|
| 904 |
+
versions.insert(0, self.oslevel_info())
|
| 905 |
+
elif self.id() == "debian" or "debian" in self.like().split():
|
| 906 |
+
# On Debian-like, add debian_version file content to candidates list.
|
| 907 |
+
versions.append(self._debian_version)
|
| 908 |
+
version = ""
|
| 909 |
+
if best:
|
| 910 |
+
# This algorithm uses the last version in priority order that has
|
| 911 |
+
# the best precision. If the versions are not in conflict, that
|
| 912 |
+
# does not matter; otherwise, using the last one instead of the
|
| 913 |
+
# first one might be considered a surprise.
|
| 914 |
+
for v in versions:
|
| 915 |
+
if v.count(".") > version.count(".") or version == "":
|
| 916 |
+
version = v
|
| 917 |
+
else:
|
| 918 |
+
for v in versions:
|
| 919 |
+
if v != "":
|
| 920 |
+
version = v
|
| 921 |
+
break
|
| 922 |
+
if pretty and version and self.codename():
|
| 923 |
+
version = f"{version} ({self.codename()})"
|
| 924 |
+
return version
|
| 925 |
+
|
| 926 |
+
def version_parts(self, best: bool = False) -> Tuple[str, str, str]:
|
| 927 |
+
"""
|
| 928 |
+
Return the version of the OS distribution, as a tuple of version
|
| 929 |
+
numbers.
|
| 930 |
+
|
| 931 |
+
For details, see :func:`distro.version_parts`.
|
| 932 |
+
"""
|
| 933 |
+
version_str = self.version(best=best)
|
| 934 |
+
if version_str:
|
| 935 |
+
version_regex = re.compile(r"(\d+)\.?(\d+)?\.?(\d+)?")
|
| 936 |
+
matches = version_regex.match(version_str)
|
| 937 |
+
if matches:
|
| 938 |
+
major, minor, build_number = matches.groups()
|
| 939 |
+
return major, minor or "", build_number or ""
|
| 940 |
+
return "", "", ""
|
| 941 |
+
|
| 942 |
+
def major_version(self, best: bool = False) -> str:
|
| 943 |
+
"""
|
| 944 |
+
Return the major version number of the current distribution.
|
| 945 |
+
|
| 946 |
+
For details, see :func:`distro.major_version`.
|
| 947 |
+
"""
|
| 948 |
+
return self.version_parts(best)[0]
|
| 949 |
+
|
| 950 |
+
def minor_version(self, best: bool = False) -> str:
|
| 951 |
+
"""
|
| 952 |
+
Return the minor version number of the current distribution.
|
| 953 |
+
|
| 954 |
+
For details, see :func:`distro.minor_version`.
|
| 955 |
+
"""
|
| 956 |
+
return self.version_parts(best)[1]
|
| 957 |
+
|
| 958 |
+
def build_number(self, best: bool = False) -> str:
|
| 959 |
+
"""
|
| 960 |
+
Return the build number of the current distribution.
|
| 961 |
+
|
| 962 |
+
For details, see :func:`distro.build_number`.
|
| 963 |
+
"""
|
| 964 |
+
return self.version_parts(best)[2]
|
| 965 |
+
|
| 966 |
+
def like(self) -> str:
|
| 967 |
+
"""
|
| 968 |
+
Return the IDs of distributions that are like the OS distribution.
|
| 969 |
+
|
| 970 |
+
For details, see :func:`distro.like`.
|
| 971 |
+
"""
|
| 972 |
+
return self.os_release_attr("id_like") or ""
|
| 973 |
+
|
| 974 |
+
def codename(self) -> str:
|
| 975 |
+
"""
|
| 976 |
+
Return the codename of the OS distribution.
|
| 977 |
+
|
| 978 |
+
For details, see :func:`distro.codename`.
|
| 979 |
+
"""
|
| 980 |
+
try:
|
| 981 |
+
# Handle os_release specially since distros might purposefully set
|
| 982 |
+
# this to empty string to have no codename
|
| 983 |
+
return self._os_release_info["codename"]
|
| 984 |
+
except KeyError:
|
| 985 |
+
return (
|
| 986 |
+
self.lsb_release_attr("codename")
|
| 987 |
+
or self.distro_release_attr("codename")
|
| 988 |
+
or ""
|
| 989 |
+
)
|
| 990 |
+
|
| 991 |
+
def info(self, pretty: bool = False, best: bool = False) -> InfoDict:
|
| 992 |
+
"""
|
| 993 |
+
Return certain machine-readable information about the OS
|
| 994 |
+
distribution.
|
| 995 |
+
|
| 996 |
+
For details, see :func:`distro.info`.
|
| 997 |
+
"""
|
| 998 |
+
return InfoDict(
|
| 999 |
+
id=self.id(),
|
| 1000 |
+
version=self.version(pretty, best),
|
| 1001 |
+
version_parts=VersionDict(
|
| 1002 |
+
major=self.major_version(best),
|
| 1003 |
+
minor=self.minor_version(best),
|
| 1004 |
+
build_number=self.build_number(best),
|
| 1005 |
+
),
|
| 1006 |
+
like=self.like(),
|
| 1007 |
+
codename=self.codename(),
|
| 1008 |
+
)
|
| 1009 |
+
|
| 1010 |
+
def os_release_info(self) -> Dict[str, str]:
|
| 1011 |
+
"""
|
| 1012 |
+
Return a dictionary containing key-value pairs for the information
|
| 1013 |
+
items from the os-release file data source of the OS distribution.
|
| 1014 |
+
|
| 1015 |
+
For details, see :func:`distro.os_release_info`.
|
| 1016 |
+
"""
|
| 1017 |
+
return self._os_release_info
|
| 1018 |
+
|
| 1019 |
+
def lsb_release_info(self) -> Dict[str, str]:
|
| 1020 |
+
"""
|
| 1021 |
+
Return a dictionary containing key-value pairs for the information
|
| 1022 |
+
items from the lsb_release command data source of the OS
|
| 1023 |
+
distribution.
|
| 1024 |
+
|
| 1025 |
+
For details, see :func:`distro.lsb_release_info`.
|
| 1026 |
+
"""
|
| 1027 |
+
return self._lsb_release_info
|
| 1028 |
+
|
| 1029 |
+
def distro_release_info(self) -> Dict[str, str]:
|
| 1030 |
+
"""
|
| 1031 |
+
Return a dictionary containing key-value pairs for the information
|
| 1032 |
+
items from the distro release file data source of the OS
|
| 1033 |
+
distribution.
|
| 1034 |
+
|
| 1035 |
+
For details, see :func:`distro.distro_release_info`.
|
| 1036 |
+
"""
|
| 1037 |
+
return self._distro_release_info
|
| 1038 |
+
|
| 1039 |
+
def uname_info(self) -> Dict[str, str]:
|
| 1040 |
+
"""
|
| 1041 |
+
Return a dictionary containing key-value pairs for the information
|
| 1042 |
+
items from the uname command data source of the OS distribution.
|
| 1043 |
+
|
| 1044 |
+
For details, see :func:`distro.uname_info`.
|
| 1045 |
+
"""
|
| 1046 |
+
return self._uname_info
|
| 1047 |
+
|
| 1048 |
+
def oslevel_info(self) -> str:
|
| 1049 |
+
"""
|
| 1050 |
+
Return AIX' oslevel command output.
|
| 1051 |
+
"""
|
| 1052 |
+
return self._oslevel_info
|
| 1053 |
+
|
| 1054 |
+
def os_release_attr(self, attribute: str) -> str:
|
| 1055 |
+
"""
|
| 1056 |
+
Return a single named information item from the os-release file data
|
| 1057 |
+
source of the OS distribution.
|
| 1058 |
+
|
| 1059 |
+
For details, see :func:`distro.os_release_attr`.
|
| 1060 |
+
"""
|
| 1061 |
+
return self._os_release_info.get(attribute, "")
|
| 1062 |
+
|
| 1063 |
+
def lsb_release_attr(self, attribute: str) -> str:
|
| 1064 |
+
"""
|
| 1065 |
+
Return a single named information item from the lsb_release command
|
| 1066 |
+
output data source of the OS distribution.
|
| 1067 |
+
|
| 1068 |
+
For details, see :func:`distro.lsb_release_attr`.
|
| 1069 |
+
"""
|
| 1070 |
+
return self._lsb_release_info.get(attribute, "")
|
| 1071 |
+
|
| 1072 |
+
def distro_release_attr(self, attribute: str) -> str:
|
| 1073 |
+
"""
|
| 1074 |
+
Return a single named information item from the distro release file
|
| 1075 |
+
data source of the OS distribution.
|
| 1076 |
+
|
| 1077 |
+
For details, see :func:`distro.distro_release_attr`.
|
| 1078 |
+
"""
|
| 1079 |
+
return self._distro_release_info.get(attribute, "")
|
| 1080 |
+
|
| 1081 |
+
def uname_attr(self, attribute: str) -> str:
|
| 1082 |
+
"""
|
| 1083 |
+
Return a single named information item from the uname command
|
| 1084 |
+
output data source of the OS distribution.
|
| 1085 |
+
|
| 1086 |
+
For details, see :func:`distro.uname_attr`.
|
| 1087 |
+
"""
|
| 1088 |
+
return self._uname_info.get(attribute, "")
|
| 1089 |
+
|
| 1090 |
+
@cached_property
|
| 1091 |
+
def _os_release_info(self) -> Dict[str, str]:
|
| 1092 |
+
"""
|
| 1093 |
+
Get the information items from the specified os-release file.
|
| 1094 |
+
|
| 1095 |
+
Returns:
|
| 1096 |
+
A dictionary containing all information items.
|
| 1097 |
+
"""
|
| 1098 |
+
if os.path.isfile(self.os_release_file):
|
| 1099 |
+
with open(self.os_release_file, encoding="utf-8") as release_file:
|
| 1100 |
+
return self._parse_os_release_content(release_file)
|
| 1101 |
+
return {}
|
| 1102 |
+
|
| 1103 |
+
@staticmethod
|
| 1104 |
+
def _parse_os_release_content(lines: TextIO) -> Dict[str, str]:
|
| 1105 |
+
"""
|
| 1106 |
+
Parse the lines of an os-release file.
|
| 1107 |
+
|
| 1108 |
+
Parameters:
|
| 1109 |
+
|
| 1110 |
+
* lines: Iterable through the lines in the os-release file.
|
| 1111 |
+
Each line must be a unicode string or a UTF-8 encoded byte
|
| 1112 |
+
string.
|
| 1113 |
+
|
| 1114 |
+
Returns:
|
| 1115 |
+
A dictionary containing all information items.
|
| 1116 |
+
"""
|
| 1117 |
+
props = {}
|
| 1118 |
+
lexer = shlex.shlex(lines, posix=True)
|
| 1119 |
+
lexer.whitespace_split = True
|
| 1120 |
+
|
| 1121 |
+
tokens = list(lexer)
|
| 1122 |
+
for token in tokens:
|
| 1123 |
+
# At this point, all shell-like parsing has been done (i.e.
|
| 1124 |
+
# comments processed, quotes and backslash escape sequences
|
| 1125 |
+
# processed, multi-line values assembled, trailing newlines
|
| 1126 |
+
# stripped, etc.), so the tokens are now either:
|
| 1127 |
+
# * variable assignments: var=value
|
| 1128 |
+
# * commands or their arguments (not allowed in os-release)
|
| 1129 |
+
# Ignore any tokens that are not variable assignments
|
| 1130 |
+
if "=" in token:
|
| 1131 |
+
k, v = token.split("=", 1)
|
| 1132 |
+
props[k.lower()] = v
|
| 1133 |
+
|
| 1134 |
+
if "version" in props:
|
| 1135 |
+
# extract release codename (if any) from version attribute
|
| 1136 |
+
match = re.search(r"\((\D+)\)|,\s*(\D+)", props["version"])
|
| 1137 |
+
if match:
|
| 1138 |
+
release_codename = match.group(1) or match.group(2)
|
| 1139 |
+
props["codename"] = props["release_codename"] = release_codename
|
| 1140 |
+
|
| 1141 |
+
if "version_codename" in props:
|
| 1142 |
+
# os-release added a version_codename field. Use that in
|
| 1143 |
+
# preference to anything else Note that some distros purposefully
|
| 1144 |
+
# do not have code names. They should be setting
|
| 1145 |
+
# version_codename=""
|
| 1146 |
+
props["codename"] = props["version_codename"]
|
| 1147 |
+
elif "ubuntu_codename" in props:
|
| 1148 |
+
# Same as above but a non-standard field name used on older Ubuntus
|
| 1149 |
+
props["codename"] = props["ubuntu_codename"]
|
| 1150 |
+
|
| 1151 |
+
return props
|
| 1152 |
+
|
| 1153 |
+
@cached_property
|
| 1154 |
+
def _lsb_release_info(self) -> Dict[str, str]:
|
| 1155 |
+
"""
|
| 1156 |
+
Get the information items from the lsb_release command output.
|
| 1157 |
+
|
| 1158 |
+
Returns:
|
| 1159 |
+
A dictionary containing all information items.
|
| 1160 |
+
"""
|
| 1161 |
+
if not self.include_lsb:
|
| 1162 |
+
return {}
|
| 1163 |
+
try:
|
| 1164 |
+
cmd = ("lsb_release", "-a")
|
| 1165 |
+
stdout = subprocess.check_output(cmd, stderr=subprocess.DEVNULL)
|
| 1166 |
+
# Command not found or lsb_release returned error
|
| 1167 |
+
except (OSError, subprocess.CalledProcessError):
|
| 1168 |
+
return {}
|
| 1169 |
+
content = self._to_str(stdout).splitlines()
|
| 1170 |
+
return self._parse_lsb_release_content(content)
|
| 1171 |
+
|
| 1172 |
+
@staticmethod
|
| 1173 |
+
def _parse_lsb_release_content(lines: Iterable[str]) -> Dict[str, str]:
|
| 1174 |
+
"""
|
| 1175 |
+
Parse the output of the lsb_release command.
|
| 1176 |
+
|
| 1177 |
+
Parameters:
|
| 1178 |
+
|
| 1179 |
+
* lines: Iterable through the lines of the lsb_release output.
|
| 1180 |
+
Each line must be a unicode string or a UTF-8 encoded byte
|
| 1181 |
+
string.
|
| 1182 |
+
|
| 1183 |
+
Returns:
|
| 1184 |
+
A dictionary containing all information items.
|
| 1185 |
+
"""
|
| 1186 |
+
props = {}
|
| 1187 |
+
for line in lines:
|
| 1188 |
+
kv = line.strip("\n").split(":", 1)
|
| 1189 |
+
if len(kv) != 2:
|
| 1190 |
+
# Ignore lines without colon.
|
| 1191 |
+
continue
|
| 1192 |
+
k, v = kv
|
| 1193 |
+
props.update({k.replace(" ", "_").lower(): v.strip()})
|
| 1194 |
+
return props
|
| 1195 |
+
|
| 1196 |
+
@cached_property
|
| 1197 |
+
def _uname_info(self) -> Dict[str, str]:
|
| 1198 |
+
if not self.include_uname:
|
| 1199 |
+
return {}
|
| 1200 |
+
try:
|
| 1201 |
+
cmd = ("uname", "-rs")
|
| 1202 |
+
stdout = subprocess.check_output(cmd, stderr=subprocess.DEVNULL)
|
| 1203 |
+
except OSError:
|
| 1204 |
+
return {}
|
| 1205 |
+
content = self._to_str(stdout).splitlines()
|
| 1206 |
+
return self._parse_uname_content(content)
|
| 1207 |
+
|
| 1208 |
+
@cached_property
|
| 1209 |
+
def _oslevel_info(self) -> str:
|
| 1210 |
+
if not self.include_oslevel:
|
| 1211 |
+
return ""
|
| 1212 |
+
try:
|
| 1213 |
+
stdout = subprocess.check_output("oslevel", stderr=subprocess.DEVNULL)
|
| 1214 |
+
except (OSError, subprocess.CalledProcessError):
|
| 1215 |
+
return ""
|
| 1216 |
+
return self._to_str(stdout).strip()
|
| 1217 |
+
|
| 1218 |
+
@cached_property
|
| 1219 |
+
def _debian_version(self) -> str:
|
| 1220 |
+
try:
|
| 1221 |
+
with open(
|
| 1222 |
+
os.path.join(self.etc_dir, "debian_version"), encoding="ascii"
|
| 1223 |
+
) as fp:
|
| 1224 |
+
return fp.readline().rstrip()
|
| 1225 |
+
except FileNotFoundError:
|
| 1226 |
+
return ""
|
| 1227 |
+
|
| 1228 |
+
@staticmethod
|
| 1229 |
+
def _parse_uname_content(lines: Sequence[str]) -> Dict[str, str]:
|
| 1230 |
+
if not lines:
|
| 1231 |
+
return {}
|
| 1232 |
+
props = {}
|
| 1233 |
+
match = re.search(r"^([^\s]+)\s+([\d\.]+)", lines[0].strip())
|
| 1234 |
+
if match:
|
| 1235 |
+
name, version = match.groups()
|
| 1236 |
+
|
| 1237 |
+
# This is to prevent the Linux kernel version from
|
| 1238 |
+
# appearing as the 'best' version on otherwise
|
| 1239 |
+
# identifiable distributions.
|
| 1240 |
+
if name == "Linux":
|
| 1241 |
+
return {}
|
| 1242 |
+
props["id"] = name.lower()
|
| 1243 |
+
props["name"] = name
|
| 1244 |
+
props["release"] = version
|
| 1245 |
+
return props
|
| 1246 |
+
|
| 1247 |
+
@staticmethod
|
| 1248 |
+
def _to_str(bytestring: bytes) -> str:
|
| 1249 |
+
encoding = sys.getfilesystemencoding()
|
| 1250 |
+
return bytestring.decode(encoding)
|
| 1251 |
+
|
| 1252 |
+
@cached_property
|
| 1253 |
+
def _distro_release_info(self) -> Dict[str, str]:
|
| 1254 |
+
"""
|
| 1255 |
+
Get the information items from the specified distro release file.
|
| 1256 |
+
|
| 1257 |
+
Returns:
|
| 1258 |
+
A dictionary containing all information items.
|
| 1259 |
+
"""
|
| 1260 |
+
if self.distro_release_file:
|
| 1261 |
+
# If it was specified, we use it and parse what we can, even if
|
| 1262 |
+
# its file name or content does not match the expected pattern.
|
| 1263 |
+
distro_info = self._parse_distro_release_file(self.distro_release_file)
|
| 1264 |
+
basename = os.path.basename(self.distro_release_file)
|
| 1265 |
+
# The file name pattern for user-specified distro release files
|
| 1266 |
+
# is somewhat more tolerant (compared to when searching for the
|
| 1267 |
+
# file), because we want to use what was specified as best as
|
| 1268 |
+
# possible.
|
| 1269 |
+
match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename)
|
| 1270 |
+
else:
|
| 1271 |
+
try:
|
| 1272 |
+
basenames = [
|
| 1273 |
+
basename
|
| 1274 |
+
for basename in os.listdir(self.etc_dir)
|
| 1275 |
+
if basename not in _DISTRO_RELEASE_IGNORE_BASENAMES
|
| 1276 |
+
and os.path.isfile(os.path.join(self.etc_dir, basename))
|
| 1277 |
+
]
|
| 1278 |
+
# We sort for repeatability in cases where there are multiple
|
| 1279 |
+
# distro specific files; e.g. CentOS, Oracle, Enterprise all
|
| 1280 |
+
# containing `redhat-release` on top of their own.
|
| 1281 |
+
basenames.sort()
|
| 1282 |
+
except OSError:
|
| 1283 |
+
# This may occur when /etc is not readable but we can't be
|
| 1284 |
+
# sure about the *-release files. Check common entries of
|
| 1285 |
+
# /etc for information. If they turn out to not be there the
|
| 1286 |
+
# error is handled in `_parse_distro_release_file()`.
|
| 1287 |
+
basenames = _DISTRO_RELEASE_BASENAMES
|
| 1288 |
+
for basename in basenames:
|
| 1289 |
+
match = _DISTRO_RELEASE_BASENAME_PATTERN.match(basename)
|
| 1290 |
+
if match is None:
|
| 1291 |
+
continue
|
| 1292 |
+
filepath = os.path.join(self.etc_dir, basename)
|
| 1293 |
+
distro_info = self._parse_distro_release_file(filepath)
|
| 1294 |
+
# The name is always present if the pattern matches.
|
| 1295 |
+
if "name" not in distro_info:
|
| 1296 |
+
continue
|
| 1297 |
+
self.distro_release_file = filepath
|
| 1298 |
+
break
|
| 1299 |
+
else: # the loop didn't "break": no candidate.
|
| 1300 |
+
return {}
|
| 1301 |
+
|
| 1302 |
+
if match is not None:
|
| 1303 |
+
distro_info["id"] = match.group(1)
|
| 1304 |
+
|
| 1305 |
+
# CloudLinux < 7: manually enrich info with proper id.
|
| 1306 |
+
if "cloudlinux" in distro_info.get("name", "").lower():
|
| 1307 |
+
distro_info["id"] = "cloudlinux"
|
| 1308 |
+
|
| 1309 |
+
return distro_info
|
| 1310 |
+
|
| 1311 |
+
def _parse_distro_release_file(self, filepath: str) -> Dict[str, str]:
|
| 1312 |
+
"""
|
| 1313 |
+
Parse a distro release file.
|
| 1314 |
+
|
| 1315 |
+
Parameters:
|
| 1316 |
+
|
| 1317 |
+
* filepath: Path name of the distro release file.
|
| 1318 |
+
|
| 1319 |
+
Returns:
|
| 1320 |
+
A dictionary containing all information items.
|
| 1321 |
+
"""
|
| 1322 |
+
try:
|
| 1323 |
+
with open(filepath, encoding="utf-8") as fp:
|
| 1324 |
+
# Only parse the first line. For instance, on SLES there
|
| 1325 |
+
# are multiple lines. We don't want them...
|
| 1326 |
+
return self._parse_distro_release_content(fp.readline())
|
| 1327 |
+
except OSError:
|
| 1328 |
+
# Ignore not being able to read a specific, seemingly version
|
| 1329 |
+
# related file.
|
| 1330 |
+
# See https://github.com/python-distro/distro/issues/162
|
| 1331 |
+
return {}
|
| 1332 |
+
|
| 1333 |
+
@staticmethod
|
| 1334 |
+
def _parse_distro_release_content(line: str) -> Dict[str, str]:
|
| 1335 |
+
"""
|
| 1336 |
+
Parse a line from a distro release file.
|
| 1337 |
+
|
| 1338 |
+
Parameters:
|
| 1339 |
+
* line: Line from the distro release file. Must be a unicode string
|
| 1340 |
+
or a UTF-8 encoded byte string.
|
| 1341 |
+
|
| 1342 |
+
Returns:
|
| 1343 |
+
A dictionary containing all information items.
|
| 1344 |
+
"""
|
| 1345 |
+
matches = _DISTRO_RELEASE_CONTENT_REVERSED_PATTERN.match(line.strip()[::-1])
|
| 1346 |
+
distro_info = {}
|
| 1347 |
+
if matches:
|
| 1348 |
+
# regexp ensures non-None
|
| 1349 |
+
distro_info["name"] = matches.group(3)[::-1]
|
| 1350 |
+
if matches.group(2):
|
| 1351 |
+
distro_info["version_id"] = matches.group(2)[::-1]
|
| 1352 |
+
if matches.group(1):
|
| 1353 |
+
distro_info["codename"] = matches.group(1)[::-1]
|
| 1354 |
+
elif line:
|
| 1355 |
+
distro_info["name"] = line.strip()
|
| 1356 |
+
return distro_info
|
| 1357 |
+
|
| 1358 |
+
|
| 1359 |
+
_distro = LinuxDistribution()
|
| 1360 |
+
|
| 1361 |
+
|
| 1362 |
+
def main() -> None:
|
| 1363 |
+
logger = logging.getLogger(__name__)
|
| 1364 |
+
logger.setLevel(logging.DEBUG)
|
| 1365 |
+
logger.addHandler(logging.StreamHandler(sys.stdout))
|
| 1366 |
+
|
| 1367 |
+
parser = argparse.ArgumentParser(description="OS distro info tool")
|
| 1368 |
+
parser.add_argument(
|
| 1369 |
+
"--json", "-j", help="Output in machine readable format", action="store_true"
|
| 1370 |
+
)
|
| 1371 |
+
|
| 1372 |
+
parser.add_argument(
|
| 1373 |
+
"--root-dir",
|
| 1374 |
+
"-r",
|
| 1375 |
+
type=str,
|
| 1376 |
+
dest="root_dir",
|
| 1377 |
+
help="Path to the root filesystem directory (defaults to /)",
|
| 1378 |
+
)
|
| 1379 |
+
|
| 1380 |
+
args = parser.parse_args()
|
| 1381 |
+
|
| 1382 |
+
if args.root_dir:
|
| 1383 |
+
dist = LinuxDistribution(
|
| 1384 |
+
include_lsb=False,
|
| 1385 |
+
include_uname=False,
|
| 1386 |
+
include_oslevel=False,
|
| 1387 |
+
root_dir=args.root_dir,
|
| 1388 |
+
)
|
| 1389 |
+
else:
|
| 1390 |
+
dist = _distro
|
| 1391 |
+
|
| 1392 |
+
if args.json:
|
| 1393 |
+
logger.info(json.dumps(dist.info(), indent=4, sort_keys=True))
|
| 1394 |
+
else:
|
| 1395 |
+
logger.info("Name: %s", dist.name(pretty=True))
|
| 1396 |
+
distribution_version = dist.version(pretty=True)
|
| 1397 |
+
logger.info("Version: %s", distribution_version)
|
| 1398 |
+
distribution_codename = dist.codename()
|
| 1399 |
+
logger.info("Codename: %s", distribution_codename)
|
| 1400 |
+
|
| 1401 |
+
|
| 1402 |
+
if __name__ == "__main__":
|
| 1403 |
+
main()
|
.venv/lib/python3.11/site-packages/distro/py.typed
ADDED
|
File without changes
|
.venv/lib/python3.11/site-packages/gguf-0.10.0.dist-info/INSTALLER
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
pip
|
.venv/lib/python3.11/site-packages/gguf-0.10.0.dist-info/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2023 Georgi Gerganov
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
.venv/lib/python3.11/site-packages/gguf-0.10.0.dist-info/METADATA
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Metadata-Version: 2.1
|
| 2 |
+
Name: gguf
|
| 3 |
+
Version: 0.10.0
|
| 4 |
+
Summary: Read and write ML models in GGUF for GGML
|
| 5 |
+
Home-page: https://ggml.ai
|
| 6 |
+
Keywords: ggml,gguf,llama.cpp
|
| 7 |
+
Author: GGML
|
| 8 |
+
Author-email: ggml@ggml.ai
|
| 9 |
+
Requires-Python: >=3.8
|
| 10 |
+
Classifier: License :: OSI Approved :: MIT License
|
| 11 |
+
Classifier: Operating System :: OS Independent
|
| 12 |
+
Classifier: Programming Language :: Python :: 3
|
| 13 |
+
Classifier: Programming Language :: Python :: 3.8
|
| 14 |
+
Classifier: Programming Language :: Python :: 3.9
|
| 15 |
+
Classifier: Programming Language :: Python :: 3.10
|
| 16 |
+
Classifier: Programming Language :: Python :: 3.11
|
| 17 |
+
Classifier: Programming Language :: Python :: 3.12
|
| 18 |
+
Requires-Dist: numpy (>=1.17)
|
| 19 |
+
Requires-Dist: pyyaml (>=5.1)
|
| 20 |
+
Requires-Dist: tqdm (>=4.27)
|
| 21 |
+
Project-URL: Repository, https://github.com/ggerganov/llama.cpp
|
| 22 |
+
Description-Content-Type: text/markdown
|
| 23 |
+
|
| 24 |
+
## gguf
|
| 25 |
+
|
| 26 |
+
This is a Python package for writing binary files in the [GGUF](https://github.com/ggerganov/ggml/pull/302)
|
| 27 |
+
(GGML Universal File) format.
|
| 28 |
+
|
| 29 |
+
See [convert_hf_to_gguf.py](https://github.com/ggerganov/llama.cpp/blob/master/convert_hf_to_gguf.py)
|
| 30 |
+
as an example for its usage.
|
| 31 |
+
|
| 32 |
+
## Installation
|
| 33 |
+
```sh
|
| 34 |
+
pip install gguf
|
| 35 |
+
```
|
| 36 |
+
|
| 37 |
+
## API Examples/Simple Tools
|
| 38 |
+
|
| 39 |
+
[examples/writer.py](https://github.com/ggerganov/llama.cpp/blob/master/gguf-py/examples/writer.py) — Generates `example.gguf` in the current directory to demonstrate generating a GGUF file. Note that this file cannot be used as a model.
|
| 40 |
+
|
| 41 |
+
[scripts/gguf_dump.py](https://github.com/ggerganov/llama.cpp/blob/master/gguf-py/scripts/gguf_dump.py) — Dumps a GGUF file's metadata to the console.
|
| 42 |
+
|
| 43 |
+
[scripts/gguf_set_metadata.py](https://github.com/ggerganov/llama.cpp/blob/master/gguf-py/scripts/gguf_set_metadata.py) — Allows changing simple metadata values in a GGUF file by key.
|
| 44 |
+
|
| 45 |
+
[scripts/gguf_convert_endian.py](https://github.com/ggerganov/llama.cpp/blob/master/gguf-py/scripts/gguf_convert_endian.py) — Allows converting the endianness of GGUF files.
|
| 46 |
+
|
| 47 |
+
[scripts/gguf_new_metadata.py](https://github.com/ggerganov/llama.cpp/blob/master/gguf-py/scripts/gguf_new_metadata.py) — Copies a GGUF file with added/modified/removed metadata values.
|
| 48 |
+
|
| 49 |
+
## Development
|
| 50 |
+
Maintainers who participate in development of this package are advised to install it in editable mode:
|
| 51 |
+
|
| 52 |
+
```sh
|
| 53 |
+
cd /path/to/llama.cpp/gguf-py
|
| 54 |
+
|
| 55 |
+
pip install --editable .
|
| 56 |
+
```
|
| 57 |
+
|
| 58 |
+
**Note**: This may require to upgrade your Pip installation, with a message saying that editable installation currently requires `setup.py`.
|
| 59 |
+
In this case, upgrade Pip to the latest:
|
| 60 |
+
|
| 61 |
+
```sh
|
| 62 |
+
pip install --upgrade pip
|
| 63 |
+
```
|
| 64 |
+
|
| 65 |
+
## Automatic publishing with CI
|
| 66 |
+
|
| 67 |
+
There's a GitHub workflow to make a release automatically upon creation of tags in a specified format.
|
| 68 |
+
|
| 69 |
+
1. Bump the version in `pyproject.toml`.
|
| 70 |
+
2. Create a tag named `gguf-vx.x.x` where `x.x.x` is the semantic version number.
|
| 71 |
+
|
| 72 |
+
```sh
|
| 73 |
+
git tag -a gguf-v1.0.0 -m "Version 1.0 release"
|
| 74 |
+
```
|
| 75 |
+
|
| 76 |
+
3. Push the tags.
|
| 77 |
+
|
| 78 |
+
```sh
|
| 79 |
+
git push origin --tags
|
| 80 |
+
```
|
| 81 |
+
|
| 82 |
+
## Manual publishing
|
| 83 |
+
If you want to publish the package manually for any reason, you need to have `twine` and `build` installed:
|
| 84 |
+
|
| 85 |
+
```sh
|
| 86 |
+
pip install build twine
|
| 87 |
+
```
|
| 88 |
+
|
| 89 |
+
Then, follow these steps to release a new version:
|
| 90 |
+
|
| 91 |
+
1. Bump the version in `pyproject.toml`.
|
| 92 |
+
2. Build the package:
|
| 93 |
+
|
| 94 |
+
```sh
|
| 95 |
+
python -m build
|
| 96 |
+
```
|
| 97 |
+
|
| 98 |
+
3. Upload the generated distribution archives:
|
| 99 |
+
|
| 100 |
+
```sh
|
| 101 |
+
python -m twine upload dist/*
|
| 102 |
+
```
|
| 103 |
+
|
| 104 |
+
## Run Unit Tests
|
| 105 |
+
|
| 106 |
+
From root of this repository you can run this command to run all the unit tests
|
| 107 |
+
|
| 108 |
+
```bash
|
| 109 |
+
python -m unittest discover ./gguf-py -v
|
| 110 |
+
```
|
| 111 |
+
|
| 112 |
+
## TODO
|
| 113 |
+
- [ ] Include conversion scripts as command line entry points in this package.
|
| 114 |
+
|
.venv/lib/python3.11/site-packages/gguf-0.10.0.dist-info/RECORD
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
../../../bin/gguf-convert-endian,sha256=W53Q9wLM4ktx91uKrLfyuFNGS7UCbZr-mA2U66A_W0E,276
|
| 2 |
+
../../../bin/gguf-dump,sha256=v29_dyj4DF8zullVHEvLoQotNn0dgtrt8f-NjwDigFw,256
|
| 3 |
+
../../../bin/gguf-new-metadata,sha256=UwjDwictRuL-TFKI2mlnkzB8YzGJSNWJ69870J9w64Y,272
|
| 4 |
+
../../../bin/gguf-set-metadata,sha256=iqt1mYpW1z41SzKpVu0r6NLBVYPDc-k06JH7cmo-e9s,272
|
| 5 |
+
gguf-0.10.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
| 6 |
+
gguf-0.10.0.dist-info/LICENSE,sha256=73jH5mWeNMeYGU8NNE6AfHIt5wy8oTWe9UdyZh4Ryjg,1072
|
| 7 |
+
gguf-0.10.0.dist-info/METADATA,sha256=0jck7V9EOoT24ugiLaorfqFzhmN3eJM7lj-fyq8tvy8,3533
|
| 8 |
+
gguf-0.10.0.dist-info/RECORD,,
|
| 9 |
+
gguf-0.10.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
| 10 |
+
gguf-0.10.0.dist-info/entry_points.txt,sha256=6SBOHW2PZstAG6hvoQ63pyxPF8CIGjGLXZyG-NFFnAc,227
|
| 11 |
+
gguf/__init__.py,sha256=PM_AEEzX6ojGAodDt78_LIm19HRCXeA6IXpgcjINfC8,219
|
| 12 |
+
gguf/__pycache__/__init__.cpython-311.pyc,,
|
| 13 |
+
gguf/__pycache__/constants.cpython-311.pyc,,
|
| 14 |
+
gguf/__pycache__/gguf.cpython-311.pyc,,
|
| 15 |
+
gguf/__pycache__/gguf_reader.cpython-311.pyc,,
|
| 16 |
+
gguf/__pycache__/gguf_writer.cpython-311.pyc,,
|
| 17 |
+
gguf/__pycache__/lazy.cpython-311.pyc,,
|
| 18 |
+
gguf/__pycache__/metadata.cpython-311.pyc,,
|
| 19 |
+
gguf/__pycache__/quants.cpython-311.pyc,,
|
| 20 |
+
gguf/__pycache__/tensor_mapping.cpython-311.pyc,,
|
| 21 |
+
gguf/__pycache__/utility.cpython-311.pyc,,
|
| 22 |
+
gguf/__pycache__/vocab.cpython-311.pyc,,
|
| 23 |
+
gguf/constants.py,sha256=00pCXTqoWP36ZR9OcuxJfhezZfxqjUTKGCNvGMrkLGU,49158
|
| 24 |
+
gguf/gguf.py,sha256=8MDu7a0JEXhLUv_tjhYqDrWubVNc41cFvBYZbkZZenI,478
|
| 25 |
+
gguf/gguf_reader.py,sha256=0Y86qmxYfPNq3u_AiAwSnhU_G-V2SjXK2b7CM3MoPP4,12365
|
| 26 |
+
gguf/gguf_writer.py,sha256=ZWW_XwN6tyDIzBCwl6ARDwwRZ9TewNMV32IQaVYRNP8,35090
|
| 27 |
+
gguf/lazy.py,sha256=QwxFAtj-5nIm_a3mvLBezpShFuo-pjJhoAiXreK9ITQ,8601
|
| 28 |
+
gguf/metadata.py,sha256=nFEqgODwH6jYF3_jcycZkN4FjLnfXOQ7A73cLKZJb0Y,25722
|
| 29 |
+
gguf/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 30 |
+
gguf/quants.py,sha256=_6kmSprMfawH3gcPz9WbxnlXCeZQIYivf_HjfUvUkM0,57336
|
| 31 |
+
gguf/tensor_mapping.py,sha256=8CpqXKwKYFz6v11Fpzhe85hPcH18gnieh_FQsISpcVc,30893
|
| 32 |
+
gguf/utility.py,sha256=jbd1bduLFjjNAQhjghSyrRQfJ_kjiTFhRAZxm0oabZw,2934
|
| 33 |
+
gguf/vocab.py,sha256=N4PNulV8x_m7cdbmbUBF3nqQ0UXGTDi31oEIh3h8hAs,19046
|
| 34 |
+
scripts/__init__.py,sha256=-Az7fR5lhVb9GPB5U_BaDb8Xp706km53vSY7aXQYw9I,297
|
| 35 |
+
scripts/__pycache__/__init__.cpython-311.pyc,,
|
| 36 |
+
scripts/__pycache__/gguf_convert_endian.cpython-311.pyc,,
|
| 37 |
+
scripts/__pycache__/gguf_dump.cpython-311.pyc,,
|
| 38 |
+
scripts/__pycache__/gguf_hash.cpython-311.pyc,,
|
| 39 |
+
scripts/__pycache__/gguf_new_metadata.cpython-311.pyc,,
|
| 40 |
+
scripts/__pycache__/gguf_set_metadata.cpython-311.pyc,,
|
| 41 |
+
scripts/gguf_convert_endian.py,sha256=tcpe1O6xLTSetYD5T8dYcDN7cwiTWS-5sH7iFCP7ga8,5279
|
| 42 |
+
scripts/gguf_dump.py,sha256=Ej3XWrYupFOg-Bz-ff9EAN5hdmp2OCdEkd46D4PtZ58,21934
|
| 43 |
+
scripts/gguf_hash.py,sha256=DtzQ3xSdS8bHxdsp8ELQaBlaZiqqDBXzqtRSEzYTkhM,3711
|
| 44 |
+
scripts/gguf_new_metadata.py,sha256=bGuF8iXkElxIsr4mR952DHEBTosA9ob-UkF-zEqlPqo,10713
|
| 45 |
+
scripts/gguf_set_metadata.py,sha256=9jISgnh2atbwKh-68oN5b0wLxs0mYpXOWILjuqLTt-Q,4131
|
.venv/lib/python3.11/site-packages/gguf-0.10.0.dist-info/WHEEL
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Wheel-Version: 1.0
|
| 2 |
+
Generator: poetry-core 1.9.0
|
| 3 |
+
Root-Is-Purelib: true
|
| 4 |
+
Tag: py3-none-any
|
.venv/lib/python3.11/site-packages/gguf-0.10.0.dist-info/entry_points.txt
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[console_scripts]
|
| 2 |
+
gguf-convert-endian=scripts:gguf_convert_endian_entrypoint
|
| 3 |
+
gguf-dump=scripts:gguf_dump_entrypoint
|
| 4 |
+
gguf-new-metadata=scripts:gguf_new_metadata_entrypoint
|
| 5 |
+
gguf-set-metadata=scripts:gguf_set_metadata_entrypoint
|
| 6 |
+
|
.venv/lib/python3.11/site-packages/mistral_common/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
__version__ = "1.5.2"
|
.venv/lib/python3.11/site-packages/mistral_common/base.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pydantic import BaseModel, ConfigDict
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class MistralBase(BaseModel):
|
| 5 |
+
"""
|
| 6 |
+
Base class for all Mistral Pydantic models.
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
model_config = ConfigDict(extra="forbid", validate_default=True, use_enum_values=True)
|
.venv/lib/python3.11/site-packages/mistral_common/exceptions.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Optional
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class MistralCommonException(Exception):
|
| 5 |
+
message: str = "Internal server error"
|
| 6 |
+
|
| 7 |
+
def __init__(
|
| 8 |
+
self,
|
| 9 |
+
message: Optional[str] = None,
|
| 10 |
+
) -> None:
|
| 11 |
+
if message:
|
| 12 |
+
self.message = message
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class TokenizerException(MistralCommonException):
|
| 16 |
+
def __init__(self, message: str) -> None:
|
| 17 |
+
super().__init__(message)
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class UnsupportedTokenizerFeatureException(MistralCommonException):
|
| 21 |
+
def __init__(self, message: str) -> None:
|
| 22 |
+
super().__init__(message)
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
class InvalidRequestException(MistralCommonException):
|
| 26 |
+
def __init__(self, message: str) -> None:
|
| 27 |
+
super().__init__(message)
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
class InvalidSystemPromptException(MistralCommonException):
|
| 31 |
+
def __init__(self, message: str) -> None:
|
| 32 |
+
super().__init__(message)
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
class InvalidMessageStructureException(MistralCommonException):
|
| 36 |
+
def __init__(self, message: str) -> None:
|
| 37 |
+
super().__init__(message)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
class InvalidAssistantMessageException(MistralCommonException):
|
| 41 |
+
def __init__(self, message: str) -> None:
|
| 42 |
+
super().__init__(message)
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
class InvalidToolMessageException(MistralCommonException):
|
| 46 |
+
def __init__(self, message: str) -> None:
|
| 47 |
+
super().__init__(message)
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
class InvalidToolSchemaException(MistralCommonException):
|
| 51 |
+
def __init__(self, message: str) -> None:
|
| 52 |
+
super().__init__(message)
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
class InvalidUserMessageException(MistralCommonException):
|
| 56 |
+
def __init__(self, message: str) -> None:
|
| 57 |
+
super().__init__(message)
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
class InvalidFunctionCallException(MistralCommonException):
|
| 61 |
+
def __init__(self, message: str) -> None:
|
| 62 |
+
super().__init__(message)
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
class InvalidToolException(MistralCommonException):
|
| 66 |
+
def __init__(self, message: str) -> None:
|
| 67 |
+
super().__init__(message)
|
.venv/lib/python3.11/site-packages/mistral_common/multimodal.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import base64
|
| 2 |
+
import io
|
| 3 |
+
from typing import Union
|
| 4 |
+
|
| 5 |
+
import requests
|
| 6 |
+
from PIL import Image
|
| 7 |
+
from pydantic import BeforeValidator, PlainSerializer, SerializationInfo
|
| 8 |
+
from typing_extensions import Annotated
|
| 9 |
+
|
| 10 |
+
from mistral_common import __version__
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def download_image(url: str) -> Image.Image:
|
| 14 |
+
headers = {"User-Agent": f"mistral-common/{__version__}"}
|
| 15 |
+
try:
|
| 16 |
+
# Make a request to download the image
|
| 17 |
+
response = requests.get(url, headers=headers)
|
| 18 |
+
response.raise_for_status() # Raise an error for bad responses (4xx, 5xx)
|
| 19 |
+
|
| 20 |
+
# Convert the image content to a PIL Image
|
| 21 |
+
img = Image.open(io.BytesIO(response.content))
|
| 22 |
+
return img
|
| 23 |
+
|
| 24 |
+
except requests.exceptions.RequestException as e:
|
| 25 |
+
raise RuntimeError(f"Error downloading the image from {url}: {e}.")
|
| 26 |
+
except Exception as e:
|
| 27 |
+
raise RuntimeError(f"Error converting to PIL image: {e}")
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def maybe_load_image_from_str_or_bytes(x: Union[Image.Image, str, bytes]) -> Image.Image:
|
| 31 |
+
if isinstance(x, Image.Image):
|
| 32 |
+
return x
|
| 33 |
+
if isinstance(x, bytes):
|
| 34 |
+
try:
|
| 35 |
+
return Image.open(io.BytesIO(x))
|
| 36 |
+
except Exception:
|
| 37 |
+
raise RuntimeError("Encountered an error when loading image from bytes.")
|
| 38 |
+
|
| 39 |
+
try:
|
| 40 |
+
image = Image.open(io.BytesIO(base64.b64decode(x.encode("ascii"))))
|
| 41 |
+
return image
|
| 42 |
+
except Exception as e:
|
| 43 |
+
raise RuntimeError(
|
| 44 |
+
f"Encountered an error when loading image from bytes starting "
|
| 45 |
+
f"with '{x[:20]}'. Expected either a PIL.Image.Image or a base64 "
|
| 46 |
+
f"encoded string of bytes."
|
| 47 |
+
) from e
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
def serialize_image_to_byte_str(im: Image.Image, info: SerializationInfo) -> str:
|
| 51 |
+
if hasattr(info, "context"):
|
| 52 |
+
context = info.context or {}
|
| 53 |
+
else:
|
| 54 |
+
context = {}
|
| 55 |
+
|
| 56 |
+
stream = io.BytesIO()
|
| 57 |
+
im_format = im.format or "PNG"
|
| 58 |
+
im.save(stream, format=im_format)
|
| 59 |
+
im_b64 = base64.b64encode(stream.getvalue()).decode("ascii")
|
| 60 |
+
if context and (max_image_b64_len := context.get("max_image_b64_len")):
|
| 61 |
+
return im_b64[:max_image_b64_len] + "..."
|
| 62 |
+
return im_b64
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
# A normal PIL image that supports serialization to b64 bytes string
|
| 66 |
+
SerializableImage = Annotated[
|
| 67 |
+
Image.Image,
|
| 68 |
+
BeforeValidator(maybe_load_image_from_str_or_bytes),
|
| 69 |
+
PlainSerializer(serialize_image_to_byte_str),
|
| 70 |
+
]
|
.venv/lib/python3.11/site-packages/mistral_common/protocol/__init__.py
ADDED
|
File without changes
|
.venv/lib/python3.11/site-packages/mistral_common/protocol/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (196 Bytes). View file
|
|
|
.venv/lib/python3.11/site-packages/mistral_common/protocol/__pycache__/base.cpython-311.pyc
ADDED
|
Binary file (1.49 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/mistral_common/protocol/__pycache__/utils.cpython-311.pyc
ADDED
|
Binary file (476 Bytes). View file
|
|
|
.venv/lib/python3.11/site-packages/mistral_common/protocol/base.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Optional
|
| 2 |
+
|
| 3 |
+
from pydantic import Field
|
| 4 |
+
|
| 5 |
+
from mistral_common.base import MistralBase
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class UsageInfo(MistralBase):
|
| 9 |
+
prompt_tokens: int = 0
|
| 10 |
+
total_tokens: int = 0
|
| 11 |
+
completion_tokens: Optional[int] = 0
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
class BaseCompletionRequest(MistralBase):
|
| 15 |
+
temperature: float = Field(default=0.7, ge=0.0, le=1.0)
|
| 16 |
+
top_p: float = Field(default=1.0, ge=0.0, le=1.0)
|
| 17 |
+
max_tokens: Optional[int] = Field(default=None, ge=0)
|
| 18 |
+
random_seed: Optional[int] = Field(default=None, ge=0)
|
.venv/lib/python3.11/site-packages/mistral_common/protocol/instruct/__init__.py
ADDED
|
File without changes
|
.venv/lib/python3.11/site-packages/mistral_common/protocol/instruct/__pycache__/normalize.cpython-311.pyc
ADDED
|
Binary file (15.2 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/mistral_common/protocol/instruct/__pycache__/request.cpython-311.pyc
ADDED
|
Binary file (2.14 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/mistral_common/protocol/instruct/__pycache__/tool_calls.cpython-311.pyc
ADDED
|
Binary file (3.07 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/mistral_common/protocol/instruct/__pycache__/validator.cpython-311.pyc
ADDED
|
Binary file (16.9 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/mistral_common/protocol/instruct/messages.py
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from enum import Enum
|
| 2 |
+
from typing import List, Literal, Optional, TypeVar, Union
|
| 3 |
+
|
| 4 |
+
from pydantic import ConfigDict, Field
|
| 5 |
+
from typing_extensions import Annotated, TypeAlias
|
| 6 |
+
|
| 7 |
+
from mistral_common.base import MistralBase
|
| 8 |
+
from mistral_common.multimodal import SerializableImage
|
| 9 |
+
from mistral_common.protocol.instruct.tool_calls import ToolCall
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class ChunkTypes(str, Enum):
|
| 13 |
+
text = "text"
|
| 14 |
+
image = "image"
|
| 15 |
+
image_url = "image_url"
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class BaseContentChunk(MistralBase):
|
| 19 |
+
type: Literal[ChunkTypes.text, ChunkTypes.image, ChunkTypes.image_url]
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class ImageChunk(BaseContentChunk):
|
| 23 |
+
type: Literal[ChunkTypes.image] = ChunkTypes.image
|
| 24 |
+
image: SerializableImage
|
| 25 |
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
class ImageURL(MistralBase):
|
| 29 |
+
url: str
|
| 30 |
+
detail: Optional[str] = None
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
class ImageURLChunk(BaseContentChunk):
|
| 34 |
+
"""
|
| 35 |
+
{"type":"image_url","image_url":{"url":"data:image/png;base64,iVBORw0
|
| 36 |
+
"""
|
| 37 |
+
|
| 38 |
+
type: Literal[ChunkTypes.image_url] = ChunkTypes.image_url
|
| 39 |
+
image_url: Union[ImageURL, str]
|
| 40 |
+
|
| 41 |
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
| 42 |
+
|
| 43 |
+
def get_url(self) -> str:
|
| 44 |
+
if isinstance(self.image_url, ImageURL):
|
| 45 |
+
return self.image_url.url
|
| 46 |
+
return self.image_url
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
class TextChunk(BaseContentChunk):
|
| 50 |
+
type: Literal[ChunkTypes.text] = ChunkTypes.text
|
| 51 |
+
text: str
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
ContentChunk = Annotated[Union[TextChunk, ImageChunk, ImageURLChunk], Field(discriminator="type")]
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
class Roles(str, Enum):
|
| 58 |
+
system = "system"
|
| 59 |
+
user = "user"
|
| 60 |
+
assistant = "assistant"
|
| 61 |
+
tool = "tool"
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
class BaseMessage(MistralBase):
|
| 65 |
+
role: Literal[Roles.system, Roles.user, Roles.assistant, Roles.tool]
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
class UserMessage(BaseMessage):
|
| 69 |
+
role: Literal[Roles.user] = Roles.user
|
| 70 |
+
content: Union[str, List[ContentChunk]]
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
class SystemMessage(BaseMessage):
|
| 74 |
+
role: Literal[Roles.system] = Roles.system
|
| 75 |
+
content: Union[str, List[ContentChunk]]
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
class AssistantMessage(BaseMessage):
|
| 79 |
+
role: Literal[Roles.assistant] = Roles.assistant
|
| 80 |
+
content: Optional[str] = None
|
| 81 |
+
tool_calls: Optional[List[ToolCall]] = None
|
| 82 |
+
prefix: bool = False
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
class FinetuningAssistantMessage(AssistantMessage):
|
| 86 |
+
weight: Optional[float] = None
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
class ToolMessage(BaseMessage):
|
| 90 |
+
content: str
|
| 91 |
+
role: Literal[Roles.tool] = Roles.tool
|
| 92 |
+
tool_call_id: Optional[str] = None
|
| 93 |
+
|
| 94 |
+
# Deprecated in V3 tokenization
|
| 95 |
+
name: Optional[str] = None
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
ChatMessage = Annotated[Union[SystemMessage, UserMessage, AssistantMessage, ToolMessage], Field(discriminator="role")]
|
| 99 |
+
|
| 100 |
+
FinetuningMessage = Annotated[
|
| 101 |
+
Union[SystemMessage, UserMessage, FinetuningAssistantMessage, ToolMessage],
|
| 102 |
+
Field(discriminator="role"),
|
| 103 |
+
]
|
| 104 |
+
|
| 105 |
+
ChatMessageType = TypeVar("ChatMessageType", bound=ChatMessage)
|
| 106 |
+
|
| 107 |
+
# Used for type hinting in generic classes where we might override the message types
|
| 108 |
+
UserMessageType = TypeVar("UserMessageType", bound=UserMessage)
|
| 109 |
+
AssistantMessageType = TypeVar("AssistantMessageType", bound=AssistantMessage)
|
| 110 |
+
ToolMessageType = TypeVar("ToolMessageType", bound=ToolMessage)
|
| 111 |
+
SystemMessageType = TypeVar("SystemMessageType", bound=SystemMessage)
|
| 112 |
+
|
| 113 |
+
UATS: TypeAlias = Union[UserMessageType, AssistantMessageType, ToolMessageType, SystemMessageType]
|
.venv/lib/python3.11/site-packages/mistral_common/protocol/instruct/normalize.py
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
from typing import Generic, List, Optional, Sequence, Type, Union
|
| 3 |
+
|
| 4 |
+
from mistral_common.protocol.instruct.messages import (
|
| 5 |
+
UATS,
|
| 6 |
+
AssistantMessage,
|
| 7 |
+
AssistantMessageType,
|
| 8 |
+
ContentChunk,
|
| 9 |
+
FinetuningAssistantMessage,
|
| 10 |
+
Roles,
|
| 11 |
+
SystemMessage,
|
| 12 |
+
SystemMessageType,
|
| 13 |
+
TextChunk,
|
| 14 |
+
ToolMessage,
|
| 15 |
+
ToolMessageType,
|
| 16 |
+
UserMessage,
|
| 17 |
+
UserMessageType,
|
| 18 |
+
)
|
| 19 |
+
from mistral_common.protocol.instruct.request import ChatCompletionRequest
|
| 20 |
+
from mistral_common.protocol.instruct.tool_calls import FunctionCall, Tool, ToolCall
|
| 21 |
+
from mistral_common.tokens.instruct.request import InstructRequest
|
| 22 |
+
from mistral_common.tokens.tokenizers.base import InstructRequestType, TokenizerVersion
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
class InstructRequestNormalizer(
|
| 26 |
+
Generic[UserMessageType, AssistantMessageType, ToolMessageType, SystemMessageType, InstructRequestType]
|
| 27 |
+
):
|
| 28 |
+
"""
|
| 29 |
+
Takes a ChatCompletionRequest and normalizes it into an InstructRequest.
|
| 30 |
+
|
| 31 |
+
The normalization process does several things such as:
|
| 32 |
+
- Aggregate consecutive messages of the same role
|
| 33 |
+
- Aggregate system prompts
|
| 34 |
+
- Normalize json content
|
| 35 |
+
- Normalize tool calls
|
| 36 |
+
"""
|
| 37 |
+
|
| 38 |
+
system_prompt_in_begin: bool = False
|
| 39 |
+
|
| 40 |
+
def __init__(
|
| 41 |
+
self,
|
| 42 |
+
user_message_class: Type[UserMessageType],
|
| 43 |
+
assistant_message_class: Type[AssistantMessageType],
|
| 44 |
+
tool_message_class: Type[ToolMessageType],
|
| 45 |
+
system_message_class: Type[SystemMessageType],
|
| 46 |
+
instruct_request_class: Type[InstructRequestType],
|
| 47 |
+
):
|
| 48 |
+
self._user_message_class = user_message_class
|
| 49 |
+
self._assistant_message_class = assistant_message_class
|
| 50 |
+
self._tool_message_class = tool_message_class
|
| 51 |
+
self._instruct_request_class = instruct_request_class
|
| 52 |
+
# this is unused but makes creation nicer
|
| 53 |
+
self._system_message_class = system_message_class
|
| 54 |
+
|
| 55 |
+
@staticmethod
|
| 56 |
+
def normalizer() -> "InstructRequestNormalizer":
|
| 57 |
+
return InstructRequestNormalizer(
|
| 58 |
+
UserMessage,
|
| 59 |
+
AssistantMessage,
|
| 60 |
+
ToolMessage,
|
| 61 |
+
SystemMessage,
|
| 62 |
+
InstructRequest[UATS, Tool],
|
| 63 |
+
)
|
| 64 |
+
|
| 65 |
+
def _normalize_json_content(self, content: Optional[str]) -> str:
|
| 66 |
+
if content is None or len(content) == 0:
|
| 67 |
+
return "{}"
|
| 68 |
+
|
| 69 |
+
try:
|
| 70 |
+
parsed_json = json.loads(content)
|
| 71 |
+
normalized_content = json.dumps(parsed_json, ensure_ascii=False)
|
| 72 |
+
except json.JSONDecodeError:
|
| 73 |
+
normalized_content = content
|
| 74 |
+
return normalized_content
|
| 75 |
+
|
| 76 |
+
def _aggregate_content_chunks(self, content: Union[str, List[TextChunk]], chunk_join_str: str = "\n\n") -> str:
|
| 77 |
+
if isinstance(content, list):
|
| 78 |
+
return chunk_join_str.join([chunk.text for chunk in content])
|
| 79 |
+
else:
|
| 80 |
+
return content
|
| 81 |
+
|
| 82 |
+
def _aggregate_system_prompts(self, request: ChatCompletionRequest[UATS]) -> Optional[str]:
|
| 83 |
+
system_prompt: List[str] = []
|
| 84 |
+
|
| 85 |
+
for message in request.messages:
|
| 86 |
+
if message.role == Roles.system and message.content:
|
| 87 |
+
system_prompt.append(self._aggregate_content_chunks(message.content))
|
| 88 |
+
|
| 89 |
+
return "\n\n".join(system_prompt) if len(system_prompt) else None
|
| 90 |
+
|
| 91 |
+
def _aggregate_tool_messages(self, messages: List[UATS]) -> List[ToolMessageType]:
|
| 92 |
+
"""
|
| 93 |
+
We currently do not do any aggregation for tool messages, but we normalize the json content
|
| 94 |
+
"""
|
| 95 |
+
tool_messages: List[ToolMessageType] = []
|
| 96 |
+
for message in messages:
|
| 97 |
+
assert isinstance(message, self._tool_message_class), "Expected tool message"
|
| 98 |
+
content = self._aggregate_content_chunks(message.content)
|
| 99 |
+
normalized_content = self._normalize_json_content(content)
|
| 100 |
+
tool_messages.append(
|
| 101 |
+
self._tool_message_class(
|
| 102 |
+
content=normalized_content, tool_call_id=message.tool_call_id, name=message.name
|
| 103 |
+
)
|
| 104 |
+
)
|
| 105 |
+
|
| 106 |
+
return tool_messages
|
| 107 |
+
|
| 108 |
+
def _normalize_tool_call(self, tool_call: ToolCall) -> ToolCall:
|
| 109 |
+
normalized_function_aruments = self._normalize_json_content(tool_call.function.arguments)
|
| 110 |
+
return ToolCall(
|
| 111 |
+
function=FunctionCall(name=tool_call.function.name, arguments=normalized_function_aruments),
|
| 112 |
+
id=tool_call.id,
|
| 113 |
+
)
|
| 114 |
+
|
| 115 |
+
def _aggregate_assistant_messages(self, messages: List[UATS]) -> AssistantMessageType:
|
| 116 |
+
aggregated_content: List[str] = []
|
| 117 |
+
tool_calls: List[ToolCall] = []
|
| 118 |
+
prefix: bool = False
|
| 119 |
+
weight: Optional[float] = None
|
| 120 |
+
for message in messages:
|
| 121 |
+
assert isinstance(message, self._assistant_message_class), "Expected assistant message"
|
| 122 |
+
if message.tool_calls is not None and len(message.tool_calls) > 0:
|
| 123 |
+
for tool_call in message.tool_calls:
|
| 124 |
+
normalized_tool_call = self._normalize_tool_call(tool_call)
|
| 125 |
+
tool_calls.append(normalized_tool_call)
|
| 126 |
+
elif message.content:
|
| 127 |
+
aggregated_content.append(self._aggregate_content_chunks(message.content))
|
| 128 |
+
prefix |= message.prefix
|
| 129 |
+
if isinstance(message, FinetuningAssistantMessage):
|
| 130 |
+
# Only FinetuningAssistantMessage can be weighted
|
| 131 |
+
if weight is not None:
|
| 132 |
+
assert (
|
| 133 |
+
weight == message.weight
|
| 134 |
+
), "Expected weights of aggregated FinetuningAssistantMessage to be equal"
|
| 135 |
+
weight = message.weight
|
| 136 |
+
|
| 137 |
+
aggregated_message = self._assistant_message_class(
|
| 138 |
+
content="\n\n".join(aggregated_content) if len(aggregated_content) else None,
|
| 139 |
+
tool_calls=tool_calls or None,
|
| 140 |
+
prefix=prefix,
|
| 141 |
+
)
|
| 142 |
+
|
| 143 |
+
if weight is not None and hasattr(aggregated_message, "weight"):
|
| 144 |
+
aggregated_message.weight = weight
|
| 145 |
+
return aggregated_message
|
| 146 |
+
|
| 147 |
+
def _aggregate_user_messages(self, messages: List[UATS]) -> UserMessageType:
|
| 148 |
+
"""
|
| 149 |
+
Just coalesce neighboring blocks of text
|
| 150 |
+
"""
|
| 151 |
+
all_content: List[ContentChunk] = []
|
| 152 |
+
text_chunks: List[str] = []
|
| 153 |
+
for message in messages:
|
| 154 |
+
assert isinstance(message, self._user_message_class), f"Expected user message got {type(message)}"
|
| 155 |
+
if isinstance(message.content, str):
|
| 156 |
+
text_chunks.append(message.content)
|
| 157 |
+
else: # it's a List[ContentChunk]
|
| 158 |
+
for chunk in message.content:
|
| 159 |
+
if isinstance(chunk, TextChunk):
|
| 160 |
+
text_chunks.append(chunk.text)
|
| 161 |
+
else:
|
| 162 |
+
if text_chunks:
|
| 163 |
+
all_content.append(TextChunk(text="\n\n".join(text_chunks)))
|
| 164 |
+
text_chunks = []
|
| 165 |
+
all_content.append(chunk)
|
| 166 |
+
|
| 167 |
+
text_content = "\n\n".join(text_chunks) if text_chunks else ""
|
| 168 |
+
|
| 169 |
+
if not all_content:
|
| 170 |
+
# if no ContentChunk was passed, we return content as a str
|
| 171 |
+
return self._user_message_class(content=text_content)
|
| 172 |
+
|
| 173 |
+
if text_content:
|
| 174 |
+
# else we return a List of content chunks
|
| 175 |
+
all_content.append(TextChunk(text=text_content))
|
| 176 |
+
|
| 177 |
+
return self._user_message_class(content=all_content)
|
| 178 |
+
|
| 179 |
+
def _aggregate_role(self, messages: List[UATS], role: Optional[Roles]) -> Sequence[UATS]:
|
| 180 |
+
if role == Roles.tool:
|
| 181 |
+
return self._aggregate_tool_messages(messages)
|
| 182 |
+
elif role == Roles.assistant:
|
| 183 |
+
return [self._aggregate_assistant_messages(messages)]
|
| 184 |
+
elif role == Roles.user:
|
| 185 |
+
return [self._aggregate_user_messages(messages)]
|
| 186 |
+
else: # System messages are ignored
|
| 187 |
+
return []
|
| 188 |
+
|
| 189 |
+
def _aggregate_messages(self, request: ChatCompletionRequest[UATS]) -> List[UATS]:
|
| 190 |
+
aggregated_messages: List[UATS] = []
|
| 191 |
+
messages_to_aggregate: List[UATS] = []
|
| 192 |
+
current_role: Optional[Roles] = None
|
| 193 |
+
current_weight: Optional[float] = None
|
| 194 |
+
|
| 195 |
+
# Collect consecutive lists of messages with the same role and weight
|
| 196 |
+
for message in request.messages:
|
| 197 |
+
new_weight = getattr(message, "weight", None)
|
| 198 |
+
if current_role != message.role or (new_weight != current_weight):
|
| 199 |
+
aggregated_messages.extend(self._aggregate_role(messages_to_aggregate, current_role))
|
| 200 |
+
messages_to_aggregate.clear()
|
| 201 |
+
current_weight = new_weight
|
| 202 |
+
current_role = message.role
|
| 203 |
+
messages_to_aggregate.append(message)
|
| 204 |
+
|
| 205 |
+
# Add the last set of messages
|
| 206 |
+
aggregated_messages.extend(self._aggregate_role(messages_to_aggregate, current_role))
|
| 207 |
+
|
| 208 |
+
# If the first message is not a user message, or we didnt aggregate
|
| 209 |
+
# anything (all system messages) for example, add an empty user message
|
| 210 |
+
if len(aggregated_messages) == 0 or (
|
| 211 |
+
not self.system_prompt_in_begin and aggregated_messages[0].role != Roles.user
|
| 212 |
+
):
|
| 213 |
+
aggregated_messages.insert(0, self._user_message_class(content=""))
|
| 214 |
+
|
| 215 |
+
return aggregated_messages
|
| 216 |
+
|
| 217 |
+
def from_chat_completion_request(self, request: ChatCompletionRequest[UATS]) -> InstructRequestType:
|
| 218 |
+
system_prompt = self._aggregate_system_prompts(request)
|
| 219 |
+
messages = self._aggregate_messages(request)
|
| 220 |
+
|
| 221 |
+
return self._instruct_request_class(
|
| 222 |
+
messages=messages, system_prompt=system_prompt, available_tools=request.tools
|
| 223 |
+
)
|
| 224 |
+
|
| 225 |
+
|
| 226 |
+
class InstructRequestNormalizerV7(InstructRequestNormalizer):
|
| 227 |
+
system_prompt_in_begin: bool = True
|
| 228 |
+
|
| 229 |
+
@staticmethod
|
| 230 |
+
def normalizer() -> "InstructRequestNormalizerV7":
|
| 231 |
+
return InstructRequestNormalizerV7(
|
| 232 |
+
UserMessage,
|
| 233 |
+
AssistantMessage,
|
| 234 |
+
ToolMessage,
|
| 235 |
+
SystemMessage,
|
| 236 |
+
InstructRequest[UATS, Tool],
|
| 237 |
+
)
|
| 238 |
+
|
| 239 |
+
def _aggregate_role(self, messages: List[UATS], role: Optional[Roles]) -> Sequence[UATS]:
|
| 240 |
+
if role == Roles.tool:
|
| 241 |
+
return self._aggregate_tool_messages(messages)
|
| 242 |
+
elif role == Roles.assistant:
|
| 243 |
+
return [self._aggregate_assistant_messages(messages)]
|
| 244 |
+
elif role == Roles.user:
|
| 245 |
+
return [self._aggregate_user_messages(messages)]
|
| 246 |
+
elif role == Roles.system:
|
| 247 |
+
return messages
|
| 248 |
+
else:
|
| 249 |
+
assert role is None and len(messages) == 0
|
| 250 |
+
return []
|
| 251 |
+
|
| 252 |
+
def _aggregate_system_prompts(self, request: ChatCompletionRequest[UATS]) -> Optional[str]:
|
| 253 |
+
raise NotImplementedError("We should not aggregate system prompts")
|
| 254 |
+
|
| 255 |
+
def from_chat_completion_request(self, request: ChatCompletionRequest[UATS]) -> InstructRequestType: # type: ignore[type-var]
|
| 256 |
+
messages = self._aggregate_messages(request)
|
| 257 |
+
return self._instruct_request_class(messages=messages, system_prompt=None, available_tools=request.tools) # type: ignore[no-any-return]
|
| 258 |
+
|
| 259 |
+
|
| 260 |
+
def normalizer_for_tokenizer_version(version: TokenizerVersion) -> InstructRequestNormalizer:
|
| 261 |
+
if version in {TokenizerVersion.v1, TokenizerVersion.v2, TokenizerVersion.v3}:
|
| 262 |
+
return InstructRequestNormalizer.normalizer()
|
| 263 |
+
elif version == TokenizerVersion.v7:
|
| 264 |
+
return InstructRequestNormalizerV7.normalizer()
|
| 265 |
+
raise ValueError(f"Unknown tokenizer version {version}")
|
.venv/lib/python3.11/site-packages/mistral_common/protocol/instruct/request.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from enum import Enum
|
| 2 |
+
from typing import Generic, List, Optional
|
| 3 |
+
|
| 4 |
+
from pydantic import Field
|
| 5 |
+
|
| 6 |
+
from mistral_common.base import MistralBase
|
| 7 |
+
from mistral_common.protocol.base import BaseCompletionRequest
|
| 8 |
+
from mistral_common.protocol.instruct.messages import ChatMessageType
|
| 9 |
+
from mistral_common.protocol.instruct.tool_calls import Tool, ToolChoice
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class ResponseFormats(str, Enum):
|
| 13 |
+
text: str = "text"
|
| 14 |
+
json: str = "json_object"
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
class ResponseFormat(MistralBase):
|
| 18 |
+
type: ResponseFormats = ResponseFormats.text
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class ChatCompletionRequest(BaseCompletionRequest, Generic[ChatMessageType]):
|
| 22 |
+
model: Optional[str] = None
|
| 23 |
+
messages: List[ChatMessageType]
|
| 24 |
+
response_format: ResponseFormat = Field(default_factory=ResponseFormat)
|
| 25 |
+
tools: Optional[List[Tool]] = None
|
| 26 |
+
tool_choice: ToolChoice = ToolChoice.auto
|
| 27 |
+
truncate_for_context_length: bool = False
|
.venv/lib/python3.11/site-packages/mistral_common/protocol/instruct/response.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import time
|
| 2 |
+
from enum import Enum
|
| 3 |
+
from typing import List, Optional
|
| 4 |
+
|
| 5 |
+
from pydantic import Field
|
| 6 |
+
|
| 7 |
+
from mistral_common.base import MistralBase
|
| 8 |
+
from mistral_common.protocol.base import UsageInfo
|
| 9 |
+
from mistral_common.protocol.instruct.tool_calls import ToolCall
|
| 10 |
+
from mistral_common.protocol.utils import random_uuid
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
class FinishReason(str, Enum):
|
| 14 |
+
stop: str = "stop"
|
| 15 |
+
length: str = "length"
|
| 16 |
+
model_length: str = "model_length"
|
| 17 |
+
error: str = "error"
|
| 18 |
+
tool_call: str = "tool_calls"
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class ChatCompletionTokenLogprobs(MistralBase):
|
| 22 |
+
token: str
|
| 23 |
+
logprob: float
|
| 24 |
+
bytes: List[int]
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class ChatCompletionResponseChoiceLogprobs(MistralBase):
|
| 28 |
+
content: List[ChatCompletionTokenLogprobs]
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
class DeltaMessage(MistralBase):
|
| 32 |
+
role: Optional[str] = None
|
| 33 |
+
content: Optional[str] = None
|
| 34 |
+
tool_calls: Optional[List[ToolCall]] = None
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
class ChatCompletionResponseChoice(MistralBase):
|
| 38 |
+
index: int
|
| 39 |
+
message: DeltaMessage
|
| 40 |
+
finish_reason: Optional[FinishReason] = None
|
| 41 |
+
logprobs: Optional[ChatCompletionResponseChoiceLogprobs] = None
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
class ChatCompletionResponse(MistralBase):
|
| 45 |
+
id: str = Field(default_factory=lambda: f"chatcmpl-{random_uuid()}")
|
| 46 |
+
object: str = "chat.completion"
|
| 47 |
+
created: int = Field(default_factory=lambda: int(time.time()))
|
| 48 |
+
model: str
|
| 49 |
+
choices: List[ChatCompletionResponseChoice]
|
| 50 |
+
usage: UsageInfo
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
class ChatCompletionResponseStreamChoice(MistralBase):
|
| 54 |
+
index: int
|
| 55 |
+
delta: DeltaMessage
|
| 56 |
+
finish_reason: Optional[FinishReason] = None
|
| 57 |
+
logprobs: Optional[ChatCompletionResponseChoiceLogprobs] = None
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
class ChatCompletionStreamResponse(MistralBase):
|
| 61 |
+
id: str = Field(default_factory=lambda: f"chatcmpl-{random_uuid()}")
|
| 62 |
+
object: str = "chat.completion.chunk"
|
| 63 |
+
created: int = Field(default_factory=lambda: int(time.time()))
|
| 64 |
+
model: str
|
| 65 |
+
choices: List[ChatCompletionResponseStreamChoice]
|
| 66 |
+
usage: Optional[UsageInfo] = None
|
.venv/lib/python3.11/site-packages/mistral_common/protocol/instruct/tool_calls.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
from enum import Enum
|
| 3 |
+
from typing import Any, Dict, TypeVar, Union
|
| 4 |
+
|
| 5 |
+
from pydantic import field_validator
|
| 6 |
+
|
| 7 |
+
from mistral_common.base import MistralBase
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
class Function(MistralBase):
|
| 11 |
+
name: str
|
| 12 |
+
description: str = ""
|
| 13 |
+
parameters: Dict[str, Any]
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class ToolTypes(str, Enum):
|
| 17 |
+
function = "function"
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class ToolChoice(str, Enum):
|
| 21 |
+
auto: str = "auto"
|
| 22 |
+
none: str = "none"
|
| 23 |
+
any: str = "any"
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
class Tool(MistralBase):
|
| 27 |
+
type: ToolTypes = ToolTypes.function
|
| 28 |
+
function: Function
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
class FunctionCall(MistralBase):
|
| 32 |
+
name: str
|
| 33 |
+
arguments: str
|
| 34 |
+
|
| 35 |
+
@field_validator("arguments", mode="before")
|
| 36 |
+
def validate_arguments(cls, v: Union[str, Dict[str, Any]]) -> str:
|
| 37 |
+
"""
|
| 38 |
+
This is for backward compatibility
|
| 39 |
+
"""
|
| 40 |
+
if isinstance(v, dict):
|
| 41 |
+
return json.dumps(v)
|
| 42 |
+
return v
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
class ToolCall(MistralBase):
|
| 46 |
+
id: str = "null" # required for V3 tokenization
|
| 47 |
+
type: ToolTypes = ToolTypes.function
|
| 48 |
+
function: FunctionCall
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
ToolType = TypeVar("ToolType", bound=Tool)
|
.venv/lib/python3.11/site-packages/mistral_common/protocol/instruct/validator.py
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import re
|
| 2 |
+
from enum import Enum
|
| 3 |
+
from typing import Generic, List
|
| 4 |
+
|
| 5 |
+
from jsonschema import Draft7Validator, SchemaError
|
| 6 |
+
|
| 7 |
+
from mistral_common.exceptions import (
|
| 8 |
+
InvalidAssistantMessageException,
|
| 9 |
+
InvalidFunctionCallException,
|
| 10 |
+
InvalidMessageStructureException,
|
| 11 |
+
InvalidRequestException,
|
| 12 |
+
InvalidSystemPromptException,
|
| 13 |
+
InvalidToolException,
|
| 14 |
+
InvalidToolMessageException,
|
| 15 |
+
InvalidToolSchemaException,
|
| 16 |
+
)
|
| 17 |
+
from mistral_common.protocol.instruct.messages import (
|
| 18 |
+
UATS,
|
| 19 |
+
AssistantMessage,
|
| 20 |
+
AssistantMessageType,
|
| 21 |
+
FinetuningAssistantMessage,
|
| 22 |
+
Roles,
|
| 23 |
+
SystemMessageType,
|
| 24 |
+
ToolMessageType,
|
| 25 |
+
UserMessageType,
|
| 26 |
+
)
|
| 27 |
+
from mistral_common.protocol.instruct.request import ChatCompletionRequest
|
| 28 |
+
from mistral_common.protocol.instruct.tool_calls import (
|
| 29 |
+
Function,
|
| 30 |
+
FunctionCall,
|
| 31 |
+
Tool,
|
| 32 |
+
ToolCall,
|
| 33 |
+
)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
class ValidationMode(Enum):
|
| 37 |
+
serving = "serving"
|
| 38 |
+
finetuning = "finetuning"
|
| 39 |
+
test = "test"
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
class MistralRequestValidator(Generic[UserMessageType, AssistantMessageType, ToolMessageType, SystemMessageType]):
|
| 43 |
+
def __init__(self, mode: ValidationMode = ValidationMode.test):
|
| 44 |
+
self._mode = mode
|
| 45 |
+
|
| 46 |
+
def validate_messages(self, messages: List[UATS]) -> None:
|
| 47 |
+
"""
|
| 48 |
+
Validates the list of messages
|
| 49 |
+
"""
|
| 50 |
+
self._validate_message_list_structure(messages)
|
| 51 |
+
self._validate_message_list_content(messages)
|
| 52 |
+
|
| 53 |
+
def validate_request(self, request: ChatCompletionRequest) -> ChatCompletionRequest[UATS]:
|
| 54 |
+
"""
|
| 55 |
+
Validates the request
|
| 56 |
+
"""
|
| 57 |
+
|
| 58 |
+
if self._mode == ValidationMode.serving:
|
| 59 |
+
if request.model is None:
|
| 60 |
+
raise InvalidRequestException("Model name parameter is required for serving mode")
|
| 61 |
+
|
| 62 |
+
# Validate the messages
|
| 63 |
+
self.validate_messages(request.messages)
|
| 64 |
+
|
| 65 |
+
# Validate the tools
|
| 66 |
+
self._validate_tools(request.tools or [])
|
| 67 |
+
|
| 68 |
+
return request
|
| 69 |
+
|
| 70 |
+
def _validate_function(self, function: Function) -> None:
|
| 71 |
+
"""
|
| 72 |
+
Checks:
|
| 73 |
+
- That the function schema is valid
|
| 74 |
+
"""
|
| 75 |
+
try:
|
| 76 |
+
Draft7Validator.check_schema(function.parameters)
|
| 77 |
+
except SchemaError as e:
|
| 78 |
+
raise InvalidToolSchemaException(f"Invalid tool schema: {e.message}")
|
| 79 |
+
|
| 80 |
+
if not re.match(r"^[a-zA-Z0-9_-]{1,64}$", function.name):
|
| 81 |
+
raise InvalidToolException(
|
| 82 |
+
f"Function name was {function.name} but must be a-z, A-Z, 0-9, "
|
| 83 |
+
"or contain underscores and dashes, with a maximum length of 64."
|
| 84 |
+
)
|
| 85 |
+
|
| 86 |
+
def _validate_tools(self, tools: List[Tool]) -> None:
|
| 87 |
+
"""
|
| 88 |
+
Checks:
|
| 89 |
+
- That the tool schemas are valid
|
| 90 |
+
"""
|
| 91 |
+
|
| 92 |
+
for tool in tools:
|
| 93 |
+
self._validate_function(tool.function)
|
| 94 |
+
|
| 95 |
+
def _validate_user_message(self, message: UserMessageType) -> None:
|
| 96 |
+
pass
|
| 97 |
+
|
| 98 |
+
def _validate_tool_message(self, message: ToolMessageType) -> None:
|
| 99 |
+
"""
|
| 100 |
+
Checks:
|
| 101 |
+
- The tool name is valid
|
| 102 |
+
"""
|
| 103 |
+
if message.name is not None:
|
| 104 |
+
if not re.match(r"^[a-zA-Z0-9_-]{1,64}$", message.name):
|
| 105 |
+
raise InvalidToolMessageException(
|
| 106 |
+
f"Function name was {message.name} but must be a-z, A-Z, 0-9, "
|
| 107 |
+
"or contain underscores and dashes, with a maximum length of 64."
|
| 108 |
+
)
|
| 109 |
+
|
| 110 |
+
def _validate_system_message(self, message: SystemMessageType) -> None:
|
| 111 |
+
"""
|
| 112 |
+
Checks:
|
| 113 |
+
- That the system prompt has content
|
| 114 |
+
"""
|
| 115 |
+
if message.content is None:
|
| 116 |
+
raise InvalidSystemPromptException("System prompt must have content")
|
| 117 |
+
|
| 118 |
+
def _validate_function_call(self, function_call: FunctionCall) -> None:
|
| 119 |
+
"""
|
| 120 |
+
Checks:
|
| 121 |
+
- That the function call has a valid name
|
| 122 |
+
"""
|
| 123 |
+
if not re.match(r"^[a-zA-Z0-9_-]{1,64}$", function_call.name):
|
| 124 |
+
raise InvalidFunctionCallException(
|
| 125 |
+
f"Function name was {function_call.name} but must be a-z, A-Z, 0-9, "
|
| 126 |
+
"or contain underscores and dashes, with a maximum length of 64."
|
| 127 |
+
)
|
| 128 |
+
|
| 129 |
+
def _validate_tool_call(self, tool_call: ToolCall, is_last_message: bool) -> None:
|
| 130 |
+
"""
|
| 131 |
+
Checks:
|
| 132 |
+
- That the tool call has a valid function
|
| 133 |
+
"""
|
| 134 |
+
|
| 135 |
+
self._validate_function_call(tool_call.function)
|
| 136 |
+
|
| 137 |
+
def _validate_assistant_message(self, message: AssistantMessageType, is_last_message: bool = False) -> None:
|
| 138 |
+
"""
|
| 139 |
+
Checks:
|
| 140 |
+
- That the assistant message has either text or tool_calls, but not both
|
| 141 |
+
- That the tool calls are valid
|
| 142 |
+
"""
|
| 143 |
+
|
| 144 |
+
# Validate that the message has either text or tool_calls
|
| 145 |
+
# but not both and not neither.
|
| 146 |
+
if bool(message.content) == bool(message.tool_calls):
|
| 147 |
+
raise InvalidAssistantMessageException(
|
| 148 |
+
"Assistant message must have either content or tool_calls, but not both."
|
| 149 |
+
)
|
| 150 |
+
|
| 151 |
+
# If we have tool calls, validate them
|
| 152 |
+
if message.tool_calls is not None:
|
| 153 |
+
# Validate that the tool calls are valid
|
| 154 |
+
for tool_call in message.tool_calls:
|
| 155 |
+
self._validate_tool_call(tool_call, is_last_message=is_last_message)
|
| 156 |
+
|
| 157 |
+
if self._mode == ValidationMode.finetuning and isinstance(message, FinetuningAssistantMessage):
|
| 158 |
+
if message.weight is not None and message.weight not in [0, 1]:
|
| 159 |
+
raise InvalidAssistantMessageException("Assistant message weight must be either 0 or 1")
|
| 160 |
+
|
| 161 |
+
if message.prefix:
|
| 162 |
+
if not is_last_message:
|
| 163 |
+
raise InvalidAssistantMessageException("Assistant message with prefix True must be last message")
|
| 164 |
+
# note : we already validate that assistant messsage has content 3 lines up.
|
| 165 |
+
|
| 166 |
+
def _validate_tool_calls_followed_by_tool_messages(self, messages: List[UATS]) -> None:
|
| 167 |
+
"""
|
| 168 |
+
Checks:
|
| 169 |
+
- That the number of tool calls and tool messages are the same
|
| 170 |
+
- That the tool calls are followed by tool messages
|
| 171 |
+
"""
|
| 172 |
+
prev_role = None
|
| 173 |
+
expected_tool_messages = 0
|
| 174 |
+
for message in messages:
|
| 175 |
+
if prev_role is None:
|
| 176 |
+
prev_role = message.role
|
| 177 |
+
continue
|
| 178 |
+
|
| 179 |
+
if message.role == Roles.tool:
|
| 180 |
+
expected_tool_messages -= 1
|
| 181 |
+
elif message.role == Roles.assistant:
|
| 182 |
+
# if we have an assistant message and we have not received all the function calls
|
| 183 |
+
# we need to raise an exception
|
| 184 |
+
if expected_tool_messages != 0:
|
| 185 |
+
raise InvalidMessageStructureException("Not the same number of function calls and responses")
|
| 186 |
+
|
| 187 |
+
if message.tool_calls is not None:
|
| 188 |
+
# Validate that the number of function calls and responses are the same
|
| 189 |
+
expected_tool_messages = len(message.tool_calls)
|
| 190 |
+
|
| 191 |
+
prev_role = message.role
|
| 192 |
+
|
| 193 |
+
if expected_tool_messages != 0 and self._mode == ValidationMode.serving:
|
| 194 |
+
raise InvalidMessageStructureException("Not the same number of function calls and responses")
|
| 195 |
+
elif expected_tool_messages < 0 and self._mode == ValidationMode.finetuning:
|
| 196 |
+
raise InvalidMessageStructureException("More tool responses than tool calls")
|
| 197 |
+
|
| 198 |
+
def _validate_message_order(self, messages: List[UATS]) -> None:
|
| 199 |
+
"""
|
| 200 |
+
Validates the order of the messages, for example user -> assistant -> user -> assistant -> ...
|
| 201 |
+
"""
|
| 202 |
+
previous_role = None
|
| 203 |
+
for message in messages:
|
| 204 |
+
current_role = message.role
|
| 205 |
+
|
| 206 |
+
if previous_role is not None:
|
| 207 |
+
if previous_role == Roles.system:
|
| 208 |
+
expected_roles = {Roles.user, Roles.assistant, Roles.system}
|
| 209 |
+
elif previous_role == Roles.user:
|
| 210 |
+
expected_roles = {Roles.assistant, Roles.system, Roles.user}
|
| 211 |
+
elif previous_role == Roles.assistant:
|
| 212 |
+
expected_roles = {Roles.assistant, Roles.user, Roles.tool}
|
| 213 |
+
elif previous_role == Roles.tool:
|
| 214 |
+
expected_roles = {Roles.assistant, Roles.tool}
|
| 215 |
+
|
| 216 |
+
if current_role not in expected_roles:
|
| 217 |
+
raise InvalidMessageStructureException(
|
| 218 |
+
f"Unexpected role '{current_role}' after role '{previous_role}'"
|
| 219 |
+
)
|
| 220 |
+
|
| 221 |
+
previous_role = current_role
|
| 222 |
+
|
| 223 |
+
def _validate_last_message(self, message: UATS) -> None:
|
| 224 |
+
# The last message must be a user or tool message in serving mode or an assistant message in finetuning mode
|
| 225 |
+
last_message_role = message.role
|
| 226 |
+
if self._mode == ValidationMode.finetuning:
|
| 227 |
+
if last_message_role != Roles.assistant:
|
| 228 |
+
raise InvalidMessageStructureException(
|
| 229 |
+
f"Expected last role Assistant for finetuning but got {last_message_role}"
|
| 230 |
+
)
|
| 231 |
+
else:
|
| 232 |
+
bad_assistant = isinstance(message, AssistantMessage) and not message.prefix
|
| 233 |
+
bad_role = message.role not in {Roles.user, Roles.tool}
|
| 234 |
+
if bad_assistant and bad_role:
|
| 235 |
+
raise InvalidMessageStructureException(
|
| 236 |
+
f"Expected last role User or Tool (or Assistant with prefix True) for serving"
|
| 237 |
+
f" but got {last_message_role}"
|
| 238 |
+
)
|
| 239 |
+
|
| 240 |
+
def _validate_message_list_structure(self, messages: List[UATS]) -> None:
|
| 241 |
+
"""
|
| 242 |
+
Validates the structure of the list of messages
|
| 243 |
+
|
| 244 |
+
For example the messages must be in the correct order of user/assistant/tool
|
| 245 |
+
"""
|
| 246 |
+
|
| 247 |
+
if len(messages) == 0:
|
| 248 |
+
raise InvalidMessageStructureException("Conversation must have at least one message")
|
| 249 |
+
|
| 250 |
+
# If we have one message it must be a user or a system message
|
| 251 |
+
if len(messages) == 1:
|
| 252 |
+
if messages[0].role not in {Roles.user, Roles.system}:
|
| 253 |
+
raise InvalidMessageStructureException("Conversation must start with a user message or system message")
|
| 254 |
+
|
| 255 |
+
# Always check the last message if in fine-tuning mode
|
| 256 |
+
if self._mode == ValidationMode.finetuning or len(messages) > 1:
|
| 257 |
+
self._validate_last_message(messages[-1])
|
| 258 |
+
|
| 259 |
+
self._validate_message_order(messages)
|
| 260 |
+
self._validate_tool_calls_followed_by_tool_messages(messages)
|
| 261 |
+
|
| 262 |
+
def _validate_message_list_content(self, messages: List[UATS]) -> None:
|
| 263 |
+
"""
|
| 264 |
+
Validates the content of the messages
|
| 265 |
+
"""
|
| 266 |
+
|
| 267 |
+
for idx, message in enumerate(messages):
|
| 268 |
+
if message.role == Roles.user:
|
| 269 |
+
self._validate_user_message(message)
|
| 270 |
+
elif message.role == Roles.assistant:
|
| 271 |
+
self._validate_assistant_message(message, is_last_message=idx == len(messages) - 1)
|
| 272 |
+
elif message.role == Roles.tool:
|
| 273 |
+
self._validate_tool_message(message)
|
| 274 |
+
elif message.role == Roles.system:
|
| 275 |
+
self._validate_system_message(message)
|
| 276 |
+
else:
|
| 277 |
+
raise InvalidRequestException(f"Unsupported message type {type(message)}")
|
| 278 |
+
|
| 279 |
+
|
| 280 |
+
class MistralRequestValidatorV3(MistralRequestValidator):
|
| 281 |
+
def _validate_tool_message(self, message: ToolMessageType) -> None:
|
| 282 |
+
"""
|
| 283 |
+
Checks:
|
| 284 |
+
- The tool name is valid
|
| 285 |
+
- Tool call id is valid
|
| 286 |
+
"""
|
| 287 |
+
if message.name is not None:
|
| 288 |
+
if not re.match(r"^[a-zA-Z0-9_-]{1,64}$", message.name):
|
| 289 |
+
raise InvalidToolMessageException(
|
| 290 |
+
f"Function name was {message.name} but must be a-z, A-Z, 0-9, "
|
| 291 |
+
"or contain underscores and dashes, with a maximum length of 64."
|
| 292 |
+
)
|
| 293 |
+
|
| 294 |
+
if message.tool_call_id is None:
|
| 295 |
+
raise InvalidRequestException("Tool call id has to be defined.")
|
| 296 |
+
|
| 297 |
+
if not re.match(r"^[a-zA-Z0-9]{9}$", message.tool_call_id):
|
| 298 |
+
raise InvalidToolMessageException(
|
| 299 |
+
f"Tool call id was {message.tool_call_id} but must be a-z, A-Z, 0-9, with a length of 9."
|
| 300 |
+
)
|
| 301 |
+
|
| 302 |
+
def _validate_tool_call(self, tool_call: ToolCall, is_last_message: bool) -> None:
|
| 303 |
+
"""
|
| 304 |
+
Validate that the tool call has a valid ID
|
| 305 |
+
"""
|
| 306 |
+
if tool_call.id != "null":
|
| 307 |
+
if not re.match(r"^[a-zA-Z0-9]{9}$", tool_call.id):
|
| 308 |
+
raise InvalidFunctionCallException(
|
| 309 |
+
f"Tool call id was {tool_call.id} but must be a-z, A-Z, 0-9, with a length of 9."
|
| 310 |
+
)
|
| 311 |
+
if self._mode == ValidationMode.finetuning and not is_last_message and tool_call.id == "null":
|
| 312 |
+
err_message = "Tool call id of assistant message that is not last has to be defined in finetuning mode."
|
| 313 |
+
raise InvalidFunctionCallException(err_message)
|
| 314 |
+
|
| 315 |
+
if self._mode == ValidationMode.serving and tool_call.id == "null":
|
| 316 |
+
raise InvalidFunctionCallException("Tool call id has to be defined in serving mode.")
|
| 317 |
+
|
| 318 |
+
self._validate_function_call(tool_call.function)
|
| 319 |
+
|
| 320 |
+
def _validate_last_message(self, message: UATS) -> None:
|
| 321 |
+
super()._validate_last_message(message)
|
| 322 |
+
|
| 323 |
+
if self._mode == ValidationMode.finetuning:
|
| 324 |
+
# in finetuning mode it has to be an assistant message
|
| 325 |
+
# as checked by parent `_validate_last_message`
|
| 326 |
+
if message.tool_calls is not None:
|
| 327 |
+
for tool_call in message.tool_calls:
|
| 328 |
+
self._validate_tool_call(tool_call, is_last_message=True)
|
.venv/lib/python3.11/site-packages/mistral_common/protocol/utils.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import uuid
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
def random_uuid() -> str:
|
| 5 |
+
return str(uuid.uuid4().hex)
|
.venv/lib/python3.11/site-packages/mistral_common/py.typed
ADDED
|
File without changes
|
.venv/lib/python3.11/site-packages/numpy/polynomial/tests/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (195 Bytes). View file
|
|
|
.venv/lib/python3.11/site-packages/numpy/polynomial/tests/__pycache__/test_hermite_e.cpython-311.pyc
ADDED
|
Binary file (38.6 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/vllm-0.7.2.dist-info/INSTALLER
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
pip
|
.venv/lib/python3.11/site-packages/vllm-0.7.2.dist-info/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Apache License
|
| 2 |
+
Version 2.0, January 2004
|
| 3 |
+
http://www.apache.org/licenses/
|
| 4 |
+
|
| 5 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 6 |
+
|
| 7 |
+
1. Definitions.
|
| 8 |
+
|
| 9 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 10 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 11 |
+
|
| 12 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 13 |
+
the copyright owner that is granting the License.
|
| 14 |
+
|
| 15 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 16 |
+
other entities that control, are controlled by, or are under common
|
| 17 |
+
control with that entity. For the purposes of this definition,
|
| 18 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 19 |
+
direction or management of such entity, whether by contract or
|
| 20 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 21 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 22 |
+
|
| 23 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 24 |
+
exercising permissions granted by this License.
|
| 25 |
+
|
| 26 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 27 |
+
including but not limited to software source code, documentation
|
| 28 |
+
source, and configuration files.
|
| 29 |
+
|
| 30 |
+
"Object" form shall mean any form resulting from mechanical
|
| 31 |
+
transformation or translation of a Source form, including but
|
| 32 |
+
not limited to compiled object code, generated documentation,
|
| 33 |
+
and conversions to other media types.
|
| 34 |
+
|
| 35 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 36 |
+
Object form, made available under the License, as indicated by a
|
| 37 |
+
copyright notice that is included in or attached to the work
|
| 38 |
+
(an example is provided in the Appendix below).
|
| 39 |
+
|
| 40 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 41 |
+
form, that is based on (or derived from) the Work and for which the
|
| 42 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 43 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 44 |
+
of this License, Derivative Works shall not include works that remain
|
| 45 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 46 |
+
the Work and Derivative Works thereof.
|
| 47 |
+
|
| 48 |
+
"Contribution" shall mean any work of authorship, including
|
| 49 |
+
the original version of the Work and any modifications or additions
|
| 50 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 51 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 52 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 53 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 54 |
+
means any form of electronic, verbal, or written communication sent
|
| 55 |
+
to the Licensor or its representatives, including but not limited to
|
| 56 |
+
communication on electronic mailing lists, source code control systems,
|
| 57 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 58 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 59 |
+
excluding communication that is conspicuously marked or otherwise
|
| 60 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 61 |
+
|
| 62 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 63 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 64 |
+
subsequently incorporated within the Work.
|
| 65 |
+
|
| 66 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 67 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 68 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 69 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 70 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 71 |
+
Work and such Derivative Works in Source or Object form.
|
| 72 |
+
|
| 73 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 74 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 75 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 76 |
+
(except as stated in this section) patent license to make, have made,
|
| 77 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 78 |
+
where such license applies only to those patent claims licensable
|
| 79 |
+
by such Contributor that are necessarily infringed by their
|
| 80 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 81 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 82 |
+
institute patent litigation against any entity (including a
|
| 83 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 84 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 85 |
+
or contributory patent infringement, then any patent licenses
|
| 86 |
+
granted to You under this License for that Work shall terminate
|
| 87 |
+
as of the date such litigation is filed.
|
| 88 |
+
|
| 89 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 90 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 91 |
+
modifications, and in Source or Object form, provided that You
|
| 92 |
+
meet the following conditions:
|
| 93 |
+
|
| 94 |
+
(a) You must give any other recipients of the Work or
|
| 95 |
+
Derivative Works a copy of this License; and
|
| 96 |
+
|
| 97 |
+
(b) You must cause any modified files to carry prominent notices
|
| 98 |
+
stating that You changed the files; and
|
| 99 |
+
|
| 100 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 101 |
+
that You distribute, all copyright, patent, trademark, and
|
| 102 |
+
attribution notices from the Source form of the Work,
|
| 103 |
+
excluding those notices that do not pertain to any part of
|
| 104 |
+
the Derivative Works; and
|
| 105 |
+
|
| 106 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 107 |
+
distribution, then any Derivative Works that You distribute must
|
| 108 |
+
include a readable copy of the attribution notices contained
|
| 109 |
+
within such NOTICE file, excluding those notices that do not
|
| 110 |
+
pertain to any part of the Derivative Works, in at least one
|
| 111 |
+
of the following places: within a NOTICE text file distributed
|
| 112 |
+
as part of the Derivative Works; within the Source form or
|
| 113 |
+
documentation, if provided along with the Derivative Works; or,
|
| 114 |
+
within a display generated by the Derivative Works, if and
|
| 115 |
+
wherever such third-party notices normally appear. The contents
|
| 116 |
+
of the NOTICE file are for informational purposes only and
|
| 117 |
+
do not modify the License. You may add Your own attribution
|
| 118 |
+
notices within Derivative Works that You distribute, alongside
|
| 119 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 120 |
+
that such additional attribution notices cannot be construed
|
| 121 |
+
as modifying the License.
|
| 122 |
+
|
| 123 |
+
You may add Your own copyright statement to Your modifications and
|
| 124 |
+
may provide additional or different license terms and conditions
|
| 125 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 126 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 127 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 128 |
+
the conditions stated in this License.
|
| 129 |
+
|
| 130 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 131 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 132 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 133 |
+
this License, without any additional terms or conditions.
|
| 134 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 135 |
+
the terms of any separate license agreement you may have executed
|
| 136 |
+
with Licensor regarding such Contributions.
|
| 137 |
+
|
| 138 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 139 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 140 |
+
except as required for reasonable and customary use in describing the
|
| 141 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 142 |
+
|
| 143 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 144 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 145 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 146 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 147 |
+
implied, including, without limitation, any warranties or conditions
|
| 148 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 149 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 150 |
+
appropriateness of using or redistributing the Work and assume any
|
| 151 |
+
risks associated with Your exercise of permissions under this License.
|
| 152 |
+
|
| 153 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 154 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 155 |
+
unless required by applicable law (such as deliberate and grossly
|
| 156 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 157 |
+
liable to You for damages, including any direct, indirect, special,
|
| 158 |
+
incidental, or consequential damages of any character arising as a
|
| 159 |
+
result of this License or out of the use or inability to use the
|
| 160 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 161 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 162 |
+
other commercial damages or losses), even if such Contributor
|
| 163 |
+
has been advised of the possibility of such damages.
|
| 164 |
+
|
| 165 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 166 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 167 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 168 |
+
or other liability obligations and/or rights consistent with this
|
| 169 |
+
License. However, in accepting such obligations, You may act only
|
| 170 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 171 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 172 |
+
defend, and hold each Contributor harmless for any liability
|
| 173 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 174 |
+
of your accepting any such warranty or additional liability.
|
| 175 |
+
|
| 176 |
+
END OF TERMS AND CONDITIONS
|
| 177 |
+
|
| 178 |
+
APPENDIX: How to apply the Apache License to your work.
|
| 179 |
+
|
| 180 |
+
To apply the Apache License to your work, attach the following
|
| 181 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
| 182 |
+
replaced with your own identifying information. (Don't include
|
| 183 |
+
the brackets!) The text should be enclosed in the appropriate
|
| 184 |
+
comment syntax for the file format. We also recommend that a
|
| 185 |
+
file or class name and description of purpose be included on the
|
| 186 |
+
same "printed page" as the copyright notice for easier
|
| 187 |
+
identification within third-party archives.
|
| 188 |
+
|
| 189 |
+
Copyright [yyyy] [name of copyright owner]
|
| 190 |
+
|
| 191 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 192 |
+
you may not use this file except in compliance with the License.
|
| 193 |
+
You may obtain a copy of the License at
|
| 194 |
+
|
| 195 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 196 |
+
|
| 197 |
+
Unless required by applicable law or agreed to in writing, software
|
| 198 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 199 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 200 |
+
See the License for the specific language governing permissions and
|
| 201 |
+
limitations under the License.
|
.venv/lib/python3.11/site-packages/vllm-0.7.2.dist-info/METADATA
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Metadata-Version: 2.2
|
| 2 |
+
Name: vllm
|
| 3 |
+
Version: 0.7.2
|
| 4 |
+
Summary: A high-throughput and memory-efficient inference and serving engine for LLMs
|
| 5 |
+
Home-page: https://github.com/vllm-project/vllm
|
| 6 |
+
Author: vLLM Team
|
| 7 |
+
License: Apache 2.0
|
| 8 |
+
Project-URL: Homepage, https://github.com/vllm-project/vllm
|
| 9 |
+
Project-URL: Documentation, https://vllm.readthedocs.io/en/latest/
|
| 10 |
+
Classifier: Programming Language :: Python :: 3.9
|
| 11 |
+
Classifier: Programming Language :: Python :: 3.10
|
| 12 |
+
Classifier: Programming Language :: Python :: 3.11
|
| 13 |
+
Classifier: Programming Language :: Python :: 3.12
|
| 14 |
+
Classifier: License :: OSI Approved :: Apache Software License
|
| 15 |
+
Classifier: Intended Audience :: Developers
|
| 16 |
+
Classifier: Intended Audience :: Information Technology
|
| 17 |
+
Classifier: Intended Audience :: Science/Research
|
| 18 |
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
| 19 |
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
| 20 |
+
Requires-Python: >=3.9
|
| 21 |
+
Description-Content-Type: text/markdown
|
| 22 |
+
License-File: LICENSE
|
| 23 |
+
Requires-Dist: psutil
|
| 24 |
+
Requires-Dist: sentencepiece
|
| 25 |
+
Requires-Dist: numpy<2.0.0
|
| 26 |
+
Requires-Dist: requests>=2.26.0
|
| 27 |
+
Requires-Dist: tqdm
|
| 28 |
+
Requires-Dist: blake3
|
| 29 |
+
Requires-Dist: py-cpuinfo
|
| 30 |
+
Requires-Dist: transformers>=4.48.2
|
| 31 |
+
Requires-Dist: tokenizers>=0.19.1
|
| 32 |
+
Requires-Dist: protobuf
|
| 33 |
+
Requires-Dist: fastapi<0.113.0,>=0.107.0; python_version < "3.9"
|
| 34 |
+
Requires-Dist: fastapi!=0.113.*,!=0.114.0,>=0.107.0; python_version >= "3.9"
|
| 35 |
+
Requires-Dist: aiohttp
|
| 36 |
+
Requires-Dist: openai>=1.52.0
|
| 37 |
+
Requires-Dist: uvicorn[standard]
|
| 38 |
+
Requires-Dist: pydantic>=2.9
|
| 39 |
+
Requires-Dist: prometheus_client>=0.18.0
|
| 40 |
+
Requires-Dist: pillow
|
| 41 |
+
Requires-Dist: prometheus-fastapi-instrumentator>=7.0.0
|
| 42 |
+
Requires-Dist: tiktoken>=0.6.0
|
| 43 |
+
Requires-Dist: lm-format-enforcer<0.11,>=0.10.9
|
| 44 |
+
Requires-Dist: outlines==0.1.11
|
| 45 |
+
Requires-Dist: lark==1.2.2
|
| 46 |
+
Requires-Dist: xgrammar>=0.1.6; platform_machine == "x86_64"
|
| 47 |
+
Requires-Dist: typing_extensions>=4.10
|
| 48 |
+
Requires-Dist: filelock>=3.16.1
|
| 49 |
+
Requires-Dist: partial-json-parser
|
| 50 |
+
Requires-Dist: pyzmq
|
| 51 |
+
Requires-Dist: msgspec
|
| 52 |
+
Requires-Dist: gguf==0.10.0
|
| 53 |
+
Requires-Dist: importlib_metadata
|
| 54 |
+
Requires-Dist: mistral_common[opencv]>=1.5.0
|
| 55 |
+
Requires-Dist: pyyaml
|
| 56 |
+
Requires-Dist: six>=1.16.0; python_version > "3.11"
|
| 57 |
+
Requires-Dist: setuptools>=74.1.1; python_version > "3.11"
|
| 58 |
+
Requires-Dist: einops
|
| 59 |
+
Requires-Dist: compressed-tensors==0.9.1
|
| 60 |
+
Requires-Dist: depyf==0.18.0
|
| 61 |
+
Requires-Dist: cloudpickle
|
| 62 |
+
Requires-Dist: ray[default]>=2.9
|
| 63 |
+
Requires-Dist: nvidia-ml-py>=12.560.30
|
| 64 |
+
Requires-Dist: torch==2.5.1
|
| 65 |
+
Requires-Dist: torchaudio==2.5.1
|
| 66 |
+
Requires-Dist: torchvision==0.20.1
|
| 67 |
+
Requires-Dist: xformers==0.0.28.post3; platform_system == "Linux" and platform_machine == "x86_64"
|
| 68 |
+
Provides-Extra: tensorizer
|
| 69 |
+
Requires-Dist: tensorizer>=2.9.0; extra == "tensorizer"
|
| 70 |
+
Provides-Extra: runai
|
| 71 |
+
Requires-Dist: runai-model-streamer; extra == "runai"
|
| 72 |
+
Requires-Dist: runai-model-streamer-s3; extra == "runai"
|
| 73 |
+
Requires-Dist: boto3; extra == "runai"
|
| 74 |
+
Provides-Extra: audio
|
| 75 |
+
Requires-Dist: librosa; extra == "audio"
|
| 76 |
+
Requires-Dist: soundfile; extra == "audio"
|
| 77 |
+
Provides-Extra: video
|
| 78 |
+
Requires-Dist: decord; extra == "video"
|
| 79 |
+
Dynamic: author
|
| 80 |
+
Dynamic: classifier
|
| 81 |
+
Dynamic: description
|
| 82 |
+
Dynamic: description-content-type
|
| 83 |
+
Dynamic: home-page
|
| 84 |
+
Dynamic: license
|
| 85 |
+
Dynamic: project-url
|
| 86 |
+
Dynamic: provides-extra
|
| 87 |
+
Dynamic: requires-dist
|
| 88 |
+
Dynamic: requires-python
|
| 89 |
+
Dynamic: summary
|
| 90 |
+
|
| 91 |
+
<p align="center">
|
| 92 |
+
<picture>
|
| 93 |
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/vllm-project/vllm/main/docs/source/assets/logos/vllm-logo-text-dark.png">
|
| 94 |
+
<img alt="vLLM" src="https://raw.githubusercontent.com/vllm-project/vllm/main/docs/source/assets/logos/vllm-logo-text-light.png" width=55%>
|
| 95 |
+
</picture>
|
| 96 |
+
</p>
|
| 97 |
+
|
| 98 |
+
<h3 align="center">
|
| 99 |
+
Easy, fast, and cheap LLM serving for everyone
|
| 100 |
+
</h3>
|
| 101 |
+
|
| 102 |
+
<p align="center">
|
| 103 |
+
| <a href="https://docs.vllm.ai"><b>Documentation</b></a> | <a href="https://vllm.ai"><b>Blog</b></a> | <a href="https://arxiv.org/abs/2309.06180"><b>Paper</b></a> | <a href="https://x.com/vllm_project"><b>Twitter/X</b></a> | <a href="https://slack.vllm.ai"><b>Developer Slack</b></a> |
|
| 104 |
+
</p>
|
| 105 |
+
|
| 106 |
+
---
|
| 107 |
+
|
| 108 |
+
*Latest News* 🔥
|
| 109 |
+
- [2025/01] We are excited to announce the alpha release of vLLM V1: A major architectural upgrade with 1.7x speedup! Clean code, optimized execution loop, zero-overhead prefix caching, enhanced multimodal support, and more. Please check out our blog post [here](https://blog.vllm.ai/2025/01/27/v1-alpha-release.html).
|
| 110 |
+
- [2025/01] We hosted [the eighth vLLM meetup](https://lu.ma/zep56hui) with Google Cloud! Please find the meetup slides from vLLM team [here](https://docs.google.com/presentation/d/1epVkt4Zu8Jz_S5OhEHPc798emsYh2BwYfRuDDVEF7u4/edit?usp=sharing).
|
| 111 |
+
- [2024/12] vLLM joins [pytorch ecosystem](https://pytorch.org/blog/vllm-joins-pytorch)! Easy, Fast, and Cheap LLM Serving for Everyone!
|
| 112 |
+
- [2024/11] We hosted [the seventh vLLM meetup](https://lu.ma/h0qvrajz) with Snowflake! Please find the meetup slides from vLLM team [here](https://docs.google.com/presentation/d/1e3CxQBV3JsfGp30SwyvS3eM_tW-ghOhJ9PAJGK6KR54/edit?usp=sharing), and Snowflake team [here](https://docs.google.com/presentation/d/1qF3RkDAbOULwz9WK5TOltt2fE9t6uIc_hVNLFAaQX6A/edit?usp=sharing).
|
| 113 |
+
- [2024/10] We have just created a developer slack ([slack.vllm.ai](https://slack.vllm.ai)) focusing on coordinating contributions and discussing features. Please feel free to join us there!
|
| 114 |
+
- [2024/10] Ray Summit 2024 held a special track for vLLM! Please find the opening talk slides from the vLLM team [here](https://docs.google.com/presentation/d/1B_KQxpHBTRa_mDF-tR6i8rWdOU5QoTZNcEg2MKZxEHM/edit?usp=sharing). Learn more from the [talks](https://www.youtube.com/playlist?list=PLzTswPQNepXl6AQwifuwUImLPFRVpksjR) from other vLLM contributors and users!
|
| 115 |
+
- [2024/09] We hosted [the sixth vLLM meetup](https://lu.ma/87q3nvnh) with NVIDIA! Please find the meetup slides [here](https://docs.google.com/presentation/d/1wrLGwytQfaOTd5wCGSPNhoaW3nq0E-9wqyP7ny93xRs/edit?usp=sharing).
|
| 116 |
+
- [2024/07] We hosted [the fifth vLLM meetup](https://lu.ma/lp0gyjqr) with AWS! Please find the meetup slides [here](https://docs.google.com/presentation/d/1RgUD8aCfcHocghoP3zmXzck9vX3RCI9yfUAB2Bbcl4Y/edit?usp=sharing).
|
| 117 |
+
- [2024/07] In partnership with Meta, vLLM officially supports Llama 3.1 with FP8 quantization and pipeline parallelism! Please check out our blog post [here](https://blog.vllm.ai/2024/07/23/llama31.html).
|
| 118 |
+
- [2024/06] We hosted [the fourth vLLM meetup](https://lu.ma/agivllm) with Cloudflare and BentoML! Please find the meetup slides [here](https://docs.google.com/presentation/d/1iJ8o7V2bQEi0BFEljLTwc5G1S10_Rhv3beed5oB0NJ4/edit?usp=sharing).
|
| 119 |
+
- [2024/04] We hosted [the third vLLM meetup](https://robloxandvllmmeetup2024.splashthat.com/) with Roblox! Please find the meetup slides [here](https://docs.google.com/presentation/d/1A--47JAK4BJ39t954HyTkvtfwn0fkqtsL8NGFuslReM/edit?usp=sharing).
|
| 120 |
+
- [2024/01] We hosted [the second vLLM meetup](https://lu.ma/ygxbpzhl) with IBM! Please find the meetup slides [here](https://docs.google.com/presentation/d/12mI2sKABnUw5RBWXDYY-HtHth4iMSNcEoQ10jDQbxgA/edit?usp=sharing).
|
| 121 |
+
- [2023/10] We hosted [the first vLLM meetup](https://lu.ma/first-vllm-meetup) with a16z! Please find the meetup slides [here](https://docs.google.com/presentation/d/1QL-XPFXiFpDBh86DbEegFXBXFXjix4v032GhShbKf3s/edit?usp=sharing).
|
| 122 |
+
- [2023/08] We would like to express our sincere gratitude to [Andreessen Horowitz](https://a16z.com/2023/08/30/supporting-the-open-source-ai-community/) (a16z) for providing a generous grant to support the open-source development and research of vLLM.
|
| 123 |
+
- [2023/06] We officially released vLLM! FastChat-vLLM integration has powered [LMSYS Vicuna and Chatbot Arena](https://chat.lmsys.org) since mid-April. Check out our [blog post](https://vllm.ai).
|
| 124 |
+
|
| 125 |
+
---
|
| 126 |
+
## About
|
| 127 |
+
vLLM is a fast and easy-to-use library for LLM inference and serving.
|
| 128 |
+
|
| 129 |
+
Originally developed in the [Sky Computing Lab](https://sky.cs.berkeley.edu) at UC Berkeley, vLLM has evolved into a community-driven project with contributions from both academia and industry.
|
| 130 |
+
|
| 131 |
+
vLLM is fast with:
|
| 132 |
+
|
| 133 |
+
- State-of-the-art serving throughput
|
| 134 |
+
- Efficient management of attention key and value memory with [**PagedAttention**](https://blog.vllm.ai/2023/06/20/vllm.html)
|
| 135 |
+
- Continuous batching of incoming requests
|
| 136 |
+
- Fast model execution with CUDA/HIP graph
|
| 137 |
+
- Quantizations: [GPTQ](https://arxiv.org/abs/2210.17323), [AWQ](https://arxiv.org/abs/2306.00978), INT4, INT8, and FP8.
|
| 138 |
+
- Optimized CUDA kernels, including integration with FlashAttention and FlashInfer.
|
| 139 |
+
- Speculative decoding
|
| 140 |
+
- Chunked prefill
|
| 141 |
+
|
| 142 |
+
**Performance benchmark**: We include a performance benchmark at the end of [our blog post](https://blog.vllm.ai/2024/09/05/perf-update.html). It compares the performance of vLLM against other LLM serving engines ([TensorRT-LLM](https://github.com/NVIDIA/TensorRT-LLM), [SGLang](https://github.com/sgl-project/sglang) and [LMDeploy](https://github.com/InternLM/lmdeploy)). The implementation is under [nightly-benchmarks folder](.buildkite/nightly-benchmarks/) and you can [reproduce](https://github.com/vllm-project/vllm/issues/8176) this benchmark using our one-click runnable script.
|
| 143 |
+
|
| 144 |
+
vLLM is flexible and easy to use with:
|
| 145 |
+
|
| 146 |
+
- Seamless integration with popular Hugging Face models
|
| 147 |
+
- High-throughput serving with various decoding algorithms, including *parallel sampling*, *beam search*, and more
|
| 148 |
+
- Tensor parallelism and pipeline parallelism support for distributed inference
|
| 149 |
+
- Streaming outputs
|
| 150 |
+
- OpenAI-compatible API server
|
| 151 |
+
- Support NVIDIA GPUs, AMD CPUs and GPUs, Intel CPUs and GPUs, PowerPC CPUs, TPU, and AWS Neuron.
|
| 152 |
+
- Prefix caching support
|
| 153 |
+
- Multi-lora support
|
| 154 |
+
|
| 155 |
+
vLLM seamlessly supports most popular open-source models on HuggingFace, including:
|
| 156 |
+
- Transformer-like LLMs (e.g., Llama)
|
| 157 |
+
- Mixture-of-Expert LLMs (e.g., Mixtral, Deepseek-V2 and V3)
|
| 158 |
+
- Embedding Models (e.g. E5-Mistral)
|
| 159 |
+
- Multi-modal LLMs (e.g., LLaVA)
|
| 160 |
+
|
| 161 |
+
Find the full list of supported models [here](https://docs.vllm.ai/en/latest/models/supported_models.html).
|
| 162 |
+
|
| 163 |
+
## Getting Started
|
| 164 |
+
|
| 165 |
+
Install vLLM with `pip` or [from source](https://docs.vllm.ai/en/latest/getting_started/installation/gpu/index.html#build-wheel-from-source):
|
| 166 |
+
|
| 167 |
+
```bash
|
| 168 |
+
pip install vllm
|
| 169 |
+
```
|
| 170 |
+
|
| 171 |
+
Visit our [documentation](https://docs.vllm.ai/en/latest/) to learn more.
|
| 172 |
+
- [Installation](https://docs.vllm.ai/en/latest/getting_started/installation/index.html)
|
| 173 |
+
- [Quickstart](https://docs.vllm.ai/en/latest/getting_started/quickstart.html)
|
| 174 |
+
- [List of Supported Models](https://docs.vllm.ai/en/latest/models/supported_models.html)
|
| 175 |
+
|
| 176 |
+
## Contributing
|
| 177 |
+
|
| 178 |
+
We welcome and value any contributions and collaborations.
|
| 179 |
+
Please check out [CONTRIBUTING.md](./CONTRIBUTING.md) for how to get involved.
|
| 180 |
+
|
| 181 |
+
## Sponsors
|
| 182 |
+
|
| 183 |
+
vLLM is a community project. Our compute resources for development and testing are supported by the following organizations. Thank you for your support!
|
| 184 |
+
|
| 185 |
+
<!-- Note: Please sort them in alphabetical order. -->
|
| 186 |
+
<!-- Note: Please keep these consistent with docs/source/community/sponsors.md -->
|
| 187 |
+
Cash Donations:
|
| 188 |
+
- a16z
|
| 189 |
+
- Dropbox
|
| 190 |
+
- Sequoia Capital
|
| 191 |
+
- Skywork AI
|
| 192 |
+
- ZhenFund
|
| 193 |
+
|
| 194 |
+
Compute Resources:
|
| 195 |
+
- AMD
|
| 196 |
+
- Anyscale
|
| 197 |
+
- AWS
|
| 198 |
+
- Crusoe Cloud
|
| 199 |
+
- Databricks
|
| 200 |
+
- DeepInfra
|
| 201 |
+
- Google Cloud
|
| 202 |
+
- Lambda Lab
|
| 203 |
+
- Nebius
|
| 204 |
+
- Novita AI
|
| 205 |
+
- NVIDIA
|
| 206 |
+
- Replicate
|
| 207 |
+
- Roblox
|
| 208 |
+
- RunPod
|
| 209 |
+
- Trainy
|
| 210 |
+
- UC Berkeley
|
| 211 |
+
- UC San Diego
|
| 212 |
+
|
| 213 |
+
Slack Sponsor: Anyscale
|
| 214 |
+
|
| 215 |
+
We also have an official fundraising venue through [OpenCollective](https://opencollective.com/vllm). We plan to use the fund to support the development, maintenance, and adoption of vLLM.
|
| 216 |
+
|
| 217 |
+
## Citation
|
| 218 |
+
|
| 219 |
+
If you use vLLM for your research, please cite our [paper](https://arxiv.org/abs/2309.06180):
|
| 220 |
+
```bibtex
|
| 221 |
+
@inproceedings{kwon2023efficient,
|
| 222 |
+
title={Efficient Memory Management for Large Language Model Serving with PagedAttention},
|
| 223 |
+
author={Woosuk Kwon and Zhuohan Li and Siyuan Zhuang and Ying Sheng and Lianmin Zheng and Cody Hao Yu and Joseph E. Gonzalez and Hao Zhang and Ion Stoica},
|
| 224 |
+
booktitle={Proceedings of the ACM SIGOPS 29th Symposium on Operating Systems Principles},
|
| 225 |
+
year={2023}
|
| 226 |
+
}
|
| 227 |
+
```
|
| 228 |
+
|
| 229 |
+
## Contact Us
|
| 230 |
+
|
| 231 |
+
* For technical questions and feature requests, please use Github issues or discussions.
|
| 232 |
+
* For discussing with fellow users and coordinating contributions and development, please use Slack.
|
| 233 |
+
* For security disclosures, please use Github's security advisory feature.
|
| 234 |
+
* For collaborations and partnerships, please contact us at vllm-questions AT lists.berkeley.edu.
|
| 235 |
+
|
| 236 |
+
## Media Kit
|
| 237 |
+
|
| 238 |
+
* If you wish to use vLLM's logo, please refer to [our media kit repo](https://github.com/vllm-project/media-kit).
|
.venv/lib/python3.11/site-packages/vllm-0.7.2.dist-info/RECORD
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
.venv/lib/python3.11/site-packages/vllm-0.7.2.dist-info/REQUESTED
ADDED
|
File without changes
|
.venv/lib/python3.11/site-packages/vllm-0.7.2.dist-info/WHEEL
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Wheel-Version: 1.0
|
| 2 |
+
Generator: setuptools (75.8.0)
|
| 3 |
+
Root-Is-Purelib: false
|
| 4 |
+
Tag: cp38-abi3-linux_x86_64
|
| 5 |
+
|
.venv/lib/python3.11/site-packages/vllm-0.7.2.dist-info/entry_points.txt
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[console_scripts]
|
| 2 |
+
vllm = vllm.scripts:main
|
.venv/lib/python3.11/site-packages/vllm-0.7.2.dist-info/top_level.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
vllm
|
.venv/lib/python3.11/site-packages/xgrammar/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from . import testing
|
| 2 |
+
from .compiler import CompiledGrammar, GrammarCompiler
|
| 3 |
+
from .contrib import hf
|
| 4 |
+
from .grammar import Grammar, StructuralTagItem
|
| 5 |
+
from .matcher import (
|
| 6 |
+
GrammarMatcher,
|
| 7 |
+
allocate_token_bitmask,
|
| 8 |
+
apply_token_bitmask_inplace,
|
| 9 |
+
bitmask_dtype,
|
| 10 |
+
get_bitmask_shape,
|
| 11 |
+
reset_token_bitmask,
|
| 12 |
+
)
|
| 13 |
+
from .tokenizer_info import TokenizerInfo, VocabType
|
.venv/lib/python3.11/site-packages/xgrammar/__pycache__/__init__.cpython-311.pyc
ADDED
|
Binary file (835 Bytes). View file
|
|
|
.venv/lib/python3.11/site-packages/xgrammar/__pycache__/base.cpython-311.pyc
ADDED
|
Binary file (3.43 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/xgrammar/__pycache__/compiler.cpython-311.pyc
ADDED
|
Binary file (9.18 kB). View file
|
|
|
.venv/lib/python3.11/site-packages/xgrammar/__pycache__/grammar.cpython-311.pyc
ADDED
|
Binary file (14.4 kB). View file
|
|
|