We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Error when downloading model from UI
### Describe the bug

I just downloaded the latest version of text-generation-webui on Ubuntu and started the UI but it is not longer allowing me to download a model from the UI. I tried to downloading 'anon8231489123/vicuna-13b-GPTQ-4bit-128g' but got the following error:

Traceback (most recent call last): File “/home/squirol/ben2/oobabooga_linux/text-generation-webui/server.py”, line 134, in download_model_wrapper downloader = downloader_module.ModelDownloader() TypeError: ModelDownloader.init() missing 1 required positional argument: ‘max_retries’

### Is there an existing issue for this?

- [X] I have searched the existing issues

### Reproduction

1. Launch web UI using ./start_linux.sh
2. Open browser to http://127.0.0.1:7860/
3. Enter 'anon8231489123/vicuna-13b-GPTQ-4bit-128g' and select Download in UI
4. View exception under Download button

### Screenshot

![image](https://github.com/oobabooga/text-generation-webui/assets/4974765/69a6d099-cce9-4b44-90b1-e05a891de8e0)


### Logs

```shell
N/A
```


### System Info

```shell
Ubuntu
NVIDIA
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `download-model.py`
Content:
```
1 '''
2 Downloads models from Hugging Face to models/username_modelname.
3 
4 Example:
5 python download-model.py facebook/opt-1.3b
6 
7 '''
8 
9 import argparse
10 import base64
11 import datetime
12 import hashlib
13 import json
14 import os
15 import re
16 import sys
17 from pathlib import Path
18 
19 import requests
20 import tqdm
21 from requests.adapters import HTTPAdapter
22 from tqdm.contrib.concurrent import thread_map
23 
24 
25 class ModelDownloader:
26     def __init__(self, max_retries):
27         self.s = requests.Session()
28         if max_retries:
29             self.s.mount('https://cdn-lfs.huggingface.co', HTTPAdapter(max_retries=max_retries))
30             self.s.mount('https://huggingface.co', HTTPAdapter(max_retries=max_retries))
31         if os.getenv('HF_USER') is not None and os.getenv('HF_PASS') is not None:
32             self.s.auth = (os.getenv('HF_USER'), os.getenv('HF_PASS'))
33 
34     def sanitize_model_and_branch_names(self, model, branch):
35         if model[-1] == '/':
36             model = model[:-1]
37 
38         if branch is None:
39             branch = "main"
40         else:
41             pattern = re.compile(r"^[a-zA-Z0-9._-]+$")
42             if not pattern.match(branch):
43                 raise ValueError(
44                     "Invalid branch name. Only alphanumeric characters, period, underscore and dash are allowed.")
45 
46         return model, branch
47 
48     def get_download_links_from_huggingface(self, model, branch, text_only=False):
49         base = "https://huggingface.co"
50         page = f"/api/models/{model}/tree/{branch}"
51         cursor = b""
52 
53         links = []
54         sha256 = []
55         classifications = []
56         has_pytorch = False
57         has_pt = False
58         # has_ggml = False
59         has_safetensors = False
60         is_lora = False
61         while True:
62             url = f"{base}{page}" + (f"?cursor={cursor.decode()}" if cursor else "")
63             r = self.s.get(url, timeout=20)
64             r.raise_for_status()
65             content = r.content
66 
67             dict = json.loads(content)
68             if len(dict) == 0:
69                 break
70 
71             for i in range(len(dict)):
72                 fname = dict[i]['path']
73                 if not is_lora and fname.endswith(('adapter_config.json', 'adapter_model.bin')):
74                     is_lora = True
75 
76                 is_pytorch = re.match("(pytorch|adapter)_model.*\.bin", fname)
77                 is_safetensors = re.match(".*\.safetensors", fname)
78                 is_pt = re.match(".*\.pt", fname)
79                 is_ggml = re.match(".*ggml.*\.bin", fname)
80                 is_tokenizer = re.match("(tokenizer|ice).*\.model", fname)
81                 is_text = re.match(".*\.(txt|json|py|md)", fname) or is_tokenizer
82                 if any((is_pytorch, is_safetensors, is_pt, is_ggml, is_tokenizer, is_text)):
83                     if 'lfs' in dict[i]:
84                         sha256.append([fname, dict[i]['lfs']['oid']])
85 
86                     if is_text:
87                         links.append(f"https://huggingface.co/{model}/resolve/{branch}/{fname}")
88                         classifications.append('text')
89                         continue
90 
91                     if not text_only:
92                         links.append(f"https://huggingface.co/{model}/resolve/{branch}/{fname}")
93                         if is_safetensors:
94                             has_safetensors = True
95                             classifications.append('safetensors')
96                         elif is_pytorch:
97                             has_pytorch = True
98                             classifications.append('pytorch')
99                         elif is_pt:
100                             has_pt = True
101                             classifications.append('pt')
102                         elif is_ggml:
103                             # has_ggml = True
104                             classifications.append('ggml')
105 
106             cursor = base64.b64encode(f'{{"file_name":"{dict[-1]["path"]}"}}'.encode()) + b':50'
107             cursor = base64.b64encode(cursor)
108             cursor = cursor.replace(b'=', b'%3D')
109 
110         # If both pytorch and safetensors are available, download safetensors only
111         if (has_pytorch or has_pt) and has_safetensors:
112             for i in range(len(classifications) - 1, -1, -1):
113                 if classifications[i] in ['pytorch', 'pt']:
114                     links.pop(i)
115 
116         return links, sha256, is_lora
117 
118     def get_output_folder(self, model, branch, is_lora, base_folder=None):
119         if base_folder is None:
120             base_folder = 'models' if not is_lora else 'loras'
121 
122         output_folder = f"{'_'.join(model.split('/')[-2:])}"
123         if branch != 'main':
124             output_folder += f'_{branch}'
125 
126         output_folder = Path(base_folder) / output_folder
127         return output_folder
128 
129     def get_single_file(self, url, output_folder, start_from_scratch=False):
130         filename = Path(url.rsplit('/', 1)[1])
131         output_path = output_folder / filename
132         headers = {}
133         mode = 'wb'
134         if output_path.exists() and not start_from_scratch:
135 
136             # Check if the file has already been downloaded completely
137             r = self.s.get(url, stream=True, timeout=20)
138             total_size = int(r.headers.get('content-length', 0))
139             if output_path.stat().st_size >= total_size:
140                 return
141 
142             # Otherwise, resume the download from where it left off
143             headers = {'Range': f'bytes={output_path.stat().st_size}-'}
144             mode = 'ab'
145 
146         with self.s.get(url, stream=True, headers=headers, timeout=20) as r:
147             r.raise_for_status()  # Do not continue the download if the request was unsuccessful
148             total_size = int(r.headers.get('content-length', 0))
149             block_size = 1024 * 1024  # 1MB
150             with open(output_path, mode) as f:
151                 with tqdm.tqdm(total=total_size, unit='iB', unit_scale=True, bar_format='{l_bar}{bar}| {n_fmt:6}/{total_fmt:6} {rate_fmt:6}') as t:
152                     count = 0
153                     for data in r.iter_content(block_size):
154                         t.update(len(data))
155                         f.write(data)
156                         if total_size != 0 and self.progress_bar is not None:
157                             count += len(data)
158                             self.progress_bar(float(count) / float(total_size), f"Downloading {filename}")
159 
160     def start_download_threads(self, file_list, output_folder, start_from_scratch=False, threads=1):
161         thread_map(lambda url: self.get_single_file(url, output_folder, start_from_scratch=start_from_scratch), file_list, max_workers=threads, disable=True)
162 
163     def download_model_files(self, model, branch, links, sha256, output_folder, progress_bar=None, start_from_scratch=False, threads=1):
164         self.progress_bar = progress_bar
165 
166         # Creating the folder and writing the metadata
167         output_folder.mkdir(parents=True, exist_ok=True)
168         metadata = f'url: https://huggingface.co/{model}\n' \
169                    f'branch: {branch}\n' \
170                    f'download date: {datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}\n'
171 
172         sha256_str = '\n'.join([f'    {item[1]} {item[0]}' for item in sha256])
173         if sha256_str:
174             metadata += f'sha256sum:\n{sha256_str}'
175 
176         metadata += '\n'
177         (output_folder / 'huggingface-metadata.txt').write_text(metadata)
178 
179         # Downloading the files
180         print(f"Downloading the model to {output_folder}")
181         self.start_download_threads(links, output_folder, start_from_scratch=start_from_scratch, threads=threads)
182 
183     def check_model_files(self, model, branch, links, sha256, output_folder):
184         # Validate the checksums
185         validated = True
186         for i in range(len(sha256)):
187             fpath = (output_folder / sha256[i][0])
188 
189             if not fpath.exists():
190                 print(f"The following file is missing: {fpath}")
191                 validated = False
192                 continue
193 
194             with open(output_folder / sha256[i][0], "rb") as f:
195                 bytes = f.read()
196                 file_hash = hashlib.sha256(bytes).hexdigest()
197                 if file_hash != sha256[i][1]:
198                     print(f'Checksum failed: {sha256[i][0]}  {sha256[i][1]}')
199                     validated = False
200                 else:
201                     print(f'Checksum validated: {sha256[i][0]}  {sha256[i][1]}')
202 
203         if validated:
204             print('[+] Validated checksums of all model files!')
205         else:
206             print('[-] Invalid checksums. Rerun download-model.py with the --clean flag.')
207 
208 
209 if __name__ == '__main__':
210 
211     parser = argparse.ArgumentParser()
212     parser.add_argument('MODEL', type=str, default=None, nargs='?')
213     parser.add_argument('--branch', type=str, default='main', help='Name of the Git branch to download from.')
214     parser.add_argument('--threads', type=int, default=1, help='Number of files to download simultaneously.')
215     parser.add_argument('--text-only', action='store_true', help='Only download text files (txt/json).')
216     parser.add_argument('--output', type=str, default=None, help='The folder where the model should be saved.')
217     parser.add_argument('--clean', action='store_true', help='Does not resume the previous download.')
218     parser.add_argument('--check', action='store_true', help='Validates the checksums of model files.')
219     parser.add_argument('--max-retries', type=int, default=5, help='Max retries count when get error in download time.')
220     args = parser.parse_args()
221 
222     branch = args.branch
223     model = args.MODEL
224 
225     if model is None:
226         print("Error: Please specify the model you'd like to download (e.g. 'python download-model.py facebook/opt-1.3b').")
227         sys.exit()
228 
229     downloader = ModelDownloader(max_retries=args.max_retries)
230     # Cleaning up the model/branch names
231     try:
232         model, branch = downloader.sanitize_model_and_branch_names(model, branch)
233     except ValueError as err_branch:
234         print(f"Error: {err_branch}")
235         sys.exit()
236 
237     # Getting the download links from Hugging Face
238     links, sha256, is_lora = downloader.get_download_links_from_huggingface(model, branch, text_only=args.text_only)
239 
240     # Getting the output folder
241     output_folder = downloader.get_output_folder(model, branch, is_lora, base_folder=args.output)
242 
243     if args.check:
244         # Check previously downloaded files
245         downloader.check_model_files(model, branch, links, sha256, output_folder)
246     else:
247         # Download files
248         downloader.download_model_files(model, branch, links, sha256, output_folder, threads=args.threads)
249
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/download-model.py b/download-model.py
--- a/download-model.py
+++ b/download-model.py
@@ -23,7 +23,7 @@
 
 
 class ModelDownloader:
-    def __init__(self, max_retries):
+    def __init__(self, max_retries = 5):
         self.s = requests.Session()
         if max_retries:
             self.s.mount('https://cdn-lfs.huggingface.co', HTTPAdapter(max_retries=max_retries))

{"golden_diff": "diff --git a/download-model.py b/download-model.py\n--- a/download-model.py\n+++ b/download-model.py\n@@ -23,7 +23,7 @@\n \n \n class ModelDownloader:\n-    def __init__(self, max_retries):\n+    def __init__(self, max_retries = 5):\n         self.s = requests.Session()\n         if max_retries:\n             self.s.mount('https://cdn-lfs.huggingface.co', HTTPAdapter(max_retries=max_retries))\n", "issue": "Error when downloading model from UI\n### Describe the bug\n\nI just downloaded the latest version of text-generation-webui on Ubuntu and started the UI but it is not longer allowing me to download a model from the UI. I tried to downloading 'anon8231489123/vicuna-13b-GPTQ-4bit-128g' but got the following error:\r\n\r\nTraceback (most recent call last): File \u201c/home/squirol/ben2/oobabooga_linux/text-generation-webui/server.py\u201d, line 134, in download_model_wrapper downloader = downloader_module.ModelDownloader() TypeError: ModelDownloader.init() missing 1 required positional argument: \u2018max_retries\u2019\n\n### Is there an existing issue for this?\n\n- [X] I have searched the existing issues\n\n### Reproduction\n\n1. Launch web UI using ./start_linux.sh\r\n2. Open browser to http://127.0.0.1:7860/\r\n3. Enter 'anon8231489123/vicuna-13b-GPTQ-4bit-128g' and select Download in UI\r\n4. View exception under Download button\n\n### Screenshot\n\n![image](https://github.com/oobabooga/text-generation-webui/assets/4974765/69a6d099-cce9-4b44-90b1-e05a891de8e0)\r\n\n\n### Logs\n\n```shell\nN/A\n```\n\n\n### System Info\n\n```shell\nUbuntu\r\nNVIDIA\n```\n\n", "before_files": [{"content": "'''\nDownloads models from Hugging Face to models/username_modelname.\n\nExample:\npython download-model.py facebook/opt-1.3b\n\n'''\n\nimport argparse\nimport base64\nimport datetime\nimport hashlib\nimport json\nimport os\nimport re\nimport sys\nfrom pathlib import Path\n\nimport requests\nimport tqdm\nfrom requests.adapters import HTTPAdapter\nfrom tqdm.contrib.concurrent import thread_map\n\n\nclass ModelDownloader:\n    def __init__(self, max_retries):\n        self.s = requests.Session()\n        if max_retries:\n            self.s.mount('https://cdn-lfs.huggingface.co', HTTPAdapter(max_retries=max_retries))\n            self.s.mount('https://huggingface.co', HTTPAdapter(max_retries=max_retries))\n        if os.getenv('HF_USER') is not None and os.getenv('HF_PASS') is not None:\n            self.s.auth = (os.getenv('HF_USER'), os.getenv('HF_PASS'))\n\n    def sanitize_model_and_branch_names(self, model, branch):\n        if model[-1] == '/':\n            model = model[:-1]\n\n        if branch is None:\n            branch = \"main\"\n        else:\n            pattern = re.compile(r\"^[a-zA-Z0-9._-]+$\")\n            if not pattern.match(branch):\n                raise ValueError(\n                    \"Invalid branch name. Only alphanumeric characters, period, underscore and dash are allowed.\")\n\n        return model, branch\n\n    def get_download_links_from_huggingface(self, model, branch, text_only=False):\n        base = \"https://huggingface.co\"\n        page = f\"/api/models/{model}/tree/{branch}\"\n        cursor = b\"\"\n\n        links = []\n        sha256 = []\n        classifications = []\n        has_pytorch = False\n        has_pt = False\n        # has_ggml = False\n        has_safetensors = False\n        is_lora = False\n        while True:\n            url = f\"{base}{page}\" + (f\"?cursor={cursor.decode()}\" if cursor else \"\")\n            r = self.s.get(url, timeout=20)\n            r.raise_for_status()\n            content = r.content\n\n            dict = json.loads(content)\n            if len(dict) == 0:\n                break\n\n            for i in range(len(dict)):\n                fname = dict[i]['path']\n                if not is_lora and fname.endswith(('adapter_config.json', 'adapter_model.bin')):\n                    is_lora = True\n\n                is_pytorch = re.match(\"(pytorch|adapter)_model.*\\.bin\", fname)\n                is_safetensors = re.match(\".*\\.safetensors\", fname)\n                is_pt = re.match(\".*\\.pt\", fname)\n                is_ggml = re.match(\".*ggml.*\\.bin\", fname)\n                is_tokenizer = re.match(\"(tokenizer|ice).*\\.model\", fname)\n                is_text = re.match(\".*\\.(txt|json|py|md)\", fname) or is_tokenizer\n                if any((is_pytorch, is_safetensors, is_pt, is_ggml, is_tokenizer, is_text)):\n                    if 'lfs' in dict[i]:\n                        sha256.append([fname, dict[i]['lfs']['oid']])\n\n                    if is_text:\n                        links.append(f\"https://huggingface.co/{model}/resolve/{branch}/{fname}\")\n                        classifications.append('text')\n                        continue\n\n                    if not text_only:\n                        links.append(f\"https://huggingface.co/{model}/resolve/{branch}/{fname}\")\n                        if is_safetensors:\n                            has_safetensors = True\n                            classifications.append('safetensors')\n                        elif is_pytorch:\n                            has_pytorch = True\n                            classifications.append('pytorch')\n                        elif is_pt:\n                            has_pt = True\n                            classifications.append('pt')\n                        elif is_ggml:\n                            # has_ggml = True\n                            classifications.append('ggml')\n\n            cursor = base64.b64encode(f'{{\"file_name\":\"{dict[-1][\"path\"]}\"}}'.encode()) + b':50'\n            cursor = base64.b64encode(cursor)\n            cursor = cursor.replace(b'=', b'%3D')\n\n        # If both pytorch and safetensors are available, download safetensors only\n        if (has_pytorch or has_pt) and has_safetensors:\n            for i in range(len(classifications) - 1, -1, -1):\n                if classifications[i] in ['pytorch', 'pt']:\n                    links.pop(i)\n\n        return links, sha256, is_lora\n\n    def get_output_folder(self, model, branch, is_lora, base_folder=None):\n        if base_folder is None:\n            base_folder = 'models' if not is_lora else 'loras'\n\n        output_folder = f\"{'_'.join(model.split('/')[-2:])}\"\n        if branch != 'main':\n            output_folder += f'_{branch}'\n\n        output_folder = Path(base_folder) / output_folder\n        return output_folder\n\n    def get_single_file(self, url, output_folder, start_from_scratch=False):\n        filename = Path(url.rsplit('/', 1)[1])\n        output_path = output_folder / filename\n        headers = {}\n        mode = 'wb'\n        if output_path.exists() and not start_from_scratch:\n\n            # Check if the file has already been downloaded completely\n            r = self.s.get(url, stream=True, timeout=20)\n            total_size = int(r.headers.get('content-length', 0))\n            if output_path.stat().st_size >= total_size:\n                return\n\n            # Otherwise, resume the download from where it left off\n            headers = {'Range': f'bytes={output_path.stat().st_size}-'}\n            mode = 'ab'\n\n        with self.s.get(url, stream=True, headers=headers, timeout=20) as r:\n            r.raise_for_status()  # Do not continue the download if the request was unsuccessful\n            total_size = int(r.headers.get('content-length', 0))\n            block_size = 1024 * 1024  # 1MB\n            with open(output_path, mode) as f:\n                with tqdm.tqdm(total=total_size, unit='iB', unit_scale=True, bar_format='{l_bar}{bar}| {n_fmt:6}/{total_fmt:6} {rate_fmt:6}') as t:\n                    count = 0\n                    for data in r.iter_content(block_size):\n                        t.update(len(data))\n                        f.write(data)\n                        if total_size != 0 and self.progress_bar is not None:\n                            count += len(data)\n                            self.progress_bar(float(count) / float(total_size), f\"Downloading {filename}\")\n\n    def start_download_threads(self, file_list, output_folder, start_from_scratch=False, threads=1):\n        thread_map(lambda url: self.get_single_file(url, output_folder, start_from_scratch=start_from_scratch), file_list, max_workers=threads, disable=True)\n\n    def download_model_files(self, model, branch, links, sha256, output_folder, progress_bar=None, start_from_scratch=False, threads=1):\n        self.progress_bar = progress_bar\n\n        # Creating the folder and writing the metadata\n        output_folder.mkdir(parents=True, exist_ok=True)\n        metadata = f'url: https://huggingface.co/{model}\\n' \\\n                   f'branch: {branch}\\n' \\\n                   f'download date: {datetime.datetime.now().strftime(\"%Y-%m-%d %H:%M:%S\")}\\n'\n\n        sha256_str = '\\n'.join([f'    {item[1]} {item[0]}' for item in sha256])\n        if sha256_str:\n            metadata += f'sha256sum:\\n{sha256_str}'\n\n        metadata += '\\n'\n        (output_folder / 'huggingface-metadata.txt').write_text(metadata)\n\n        # Downloading the files\n        print(f\"Downloading the model to {output_folder}\")\n        self.start_download_threads(links, output_folder, start_from_scratch=start_from_scratch, threads=threads)\n\n    def check_model_files(self, model, branch, links, sha256, output_folder):\n        # Validate the checksums\n        validated = True\n        for i in range(len(sha256)):\n            fpath = (output_folder / sha256[i][0])\n\n            if not fpath.exists():\n                print(f\"The following file is missing: {fpath}\")\n                validated = False\n                continue\n\n            with open(output_folder / sha256[i][0], \"rb\") as f:\n                bytes = f.read()\n                file_hash = hashlib.sha256(bytes).hexdigest()\n                if file_hash != sha256[i][1]:\n                    print(f'Checksum failed: {sha256[i][0]}  {sha256[i][1]}')\n                    validated = False\n                else:\n                    print(f'Checksum validated: {sha256[i][0]}  {sha256[i][1]}')\n\n        if validated:\n            print('[+] Validated checksums of all model files!')\n        else:\n            print('[-] Invalid checksums. Rerun download-model.py with the --clean flag.')\n\n\nif __name__ == '__main__':\n\n    parser = argparse.ArgumentParser()\n    parser.add_argument('MODEL', type=str, default=None, nargs='?')\n    parser.add_argument('--branch', type=str, default='main', help='Name of the Git branch to download from.')\n    parser.add_argument('--threads', type=int, default=1, help='Number of files to download simultaneously.')\n    parser.add_argument('--text-only', action='store_true', help='Only download text files (txt/json).')\n    parser.add_argument('--output', type=str, default=None, help='The folder where the model should be saved.')\n    parser.add_argument('--clean', action='store_true', help='Does not resume the previous download.')\n    parser.add_argument('--check', action='store_true', help='Validates the checksums of model files.')\n    parser.add_argument('--max-retries', type=int, default=5, help='Max retries count when get error in download time.')\n    args = parser.parse_args()\n\n    branch = args.branch\n    model = args.MODEL\n\n    if model is None:\n        print(\"Error: Please specify the model you'd like to download (e.g. 'python download-model.py facebook/opt-1.3b').\")\n        sys.exit()\n\n    downloader = ModelDownloader(max_retries=args.max_retries)\n    # Cleaning up the model/branch names\n    try:\n        model, branch = downloader.sanitize_model_and_branch_names(model, branch)\n    except ValueError as err_branch:\n        print(f\"Error: {err_branch}\")\n        sys.exit()\n\n    # Getting the download links from Hugging Face\n    links, sha256, is_lora = downloader.get_download_links_from_huggingface(model, branch, text_only=args.text_only)\n\n    # Getting the output folder\n    output_folder = downloader.get_output_folder(model, branch, is_lora, base_folder=args.output)\n\n    if args.check:\n        # Check previously downloaded files\n        downloader.check_model_files(model, branch, links, sha256, output_folder)\n    else:\n        # Download files\n        downloader.download_model_files(model, branch, links, sha256, output_folder, threads=args.threads)\n", "path": "download-model.py"}], "after_files": [{"content": "'''\nDownloads models from Hugging Face to models/username_modelname.\n\nExample:\npython download-model.py facebook/opt-1.3b\n\n'''\n\nimport argparse\nimport base64\nimport datetime\nimport hashlib\nimport json\nimport os\nimport re\nimport sys\nfrom pathlib import Path\n\nimport requests\nimport tqdm\nfrom requests.adapters import HTTPAdapter\nfrom tqdm.contrib.concurrent import thread_map\n\n\nclass ModelDownloader:\n    def __init__(self, max_retries = 5):\n        self.s = requests.Session()\n        if max_retries:\n            self.s.mount('https://cdn-lfs.huggingface.co', HTTPAdapter(max_retries=max_retries))\n            self.s.mount('https://huggingface.co', HTTPAdapter(max_retries=max_retries))\n        if os.getenv('HF_USER') is not None and os.getenv('HF_PASS') is not None:\n            self.s.auth = (os.getenv('HF_USER'), os.getenv('HF_PASS'))\n\n    def sanitize_model_and_branch_names(self, model, branch):\n        if model[-1] == '/':\n            model = model[:-1]\n\n        if branch is None:\n            branch = \"main\"\n        else:\n            pattern = re.compile(r\"^[a-zA-Z0-9._-]+$\")\n            if not pattern.match(branch):\n                raise ValueError(\n                    \"Invalid branch name. Only alphanumeric characters, period, underscore and dash are allowed.\")\n\n        return model, branch\n\n    def get_download_links_from_huggingface(self, model, branch, text_only=False):\n        base = \"https://huggingface.co\"\n        page = f\"/api/models/{model}/tree/{branch}\"\n        cursor = b\"\"\n\n        links = []\n        sha256 = []\n        classifications = []\n        has_pytorch = False\n        has_pt = False\n        # has_ggml = False\n        has_safetensors = False\n        is_lora = False\n        while True:\n            url = f\"{base}{page}\" + (f\"?cursor={cursor.decode()}\" if cursor else \"\")\n            r = self.s.get(url, timeout=20)\n            r.raise_for_status()\n            content = r.content\n\n            dict = json.loads(content)\n            if len(dict) == 0:\n                break\n\n            for i in range(len(dict)):\n                fname = dict[i]['path']\n                if not is_lora and fname.endswith(('adapter_config.json', 'adapter_model.bin')):\n                    is_lora = True\n\n                is_pytorch = re.match(\"(pytorch|adapter)_model.*\\.bin\", fname)\n                is_safetensors = re.match(\".*\\.safetensors\", fname)\n                is_pt = re.match(\".*\\.pt\", fname)\n                is_ggml = re.match(\".*ggml.*\\.bin\", fname)\n                is_tokenizer = re.match(\"(tokenizer|ice).*\\.model\", fname)\n                is_text = re.match(\".*\\.(txt|json|py|md)\", fname) or is_tokenizer\n                if any((is_pytorch, is_safetensors, is_pt, is_ggml, is_tokenizer, is_text)):\n                    if 'lfs' in dict[i]:\n                        sha256.append([fname, dict[i]['lfs']['oid']])\n\n                    if is_text:\n                        links.append(f\"https://huggingface.co/{model}/resolve/{branch}/{fname}\")\n                        classifications.append('text')\n                        continue\n\n                    if not text_only:\n                        links.append(f\"https://huggingface.co/{model}/resolve/{branch}/{fname}\")\n                        if is_safetensors:\n                            has_safetensors = True\n                            classifications.append('safetensors')\n                        elif is_pytorch:\n                            has_pytorch = True\n                            classifications.append('pytorch')\n                        elif is_pt:\n                            has_pt = True\n                            classifications.append('pt')\n                        elif is_ggml:\n                            # has_ggml = True\n                            classifications.append('ggml')\n\n            cursor = base64.b64encode(f'{{\"file_name\":\"{dict[-1][\"path\"]}\"}}'.encode()) + b':50'\n            cursor = base64.b64encode(cursor)\n            cursor = cursor.replace(b'=', b'%3D')\n\n        # If both pytorch and safetensors are available, download safetensors only\n        if (has_pytorch or has_pt) and has_safetensors:\n            for i in range(len(classifications) - 1, -1, -1):\n                if classifications[i] in ['pytorch', 'pt']:\n                    links.pop(i)\n\n        return links, sha256, is_lora\n\n    def get_output_folder(self, model, branch, is_lora, base_folder=None):\n        if base_folder is None:\n            base_folder = 'models' if not is_lora else 'loras'\n\n        output_folder = f\"{'_'.join(model.split('/')[-2:])}\"\n        if branch != 'main':\n            output_folder += f'_{branch}'\n\n        output_folder = Path(base_folder) / output_folder\n        return output_folder\n\n    def get_single_file(self, url, output_folder, start_from_scratch=False):\n        filename = Path(url.rsplit('/', 1)[1])\n        output_path = output_folder / filename\n        headers = {}\n        mode = 'wb'\n        if output_path.exists() and not start_from_scratch:\n\n            # Check if the file has already been downloaded completely\n            r = self.s.get(url, stream=True, timeout=20)\n            total_size = int(r.headers.get('content-length', 0))\n            if output_path.stat().st_size >= total_size:\n                return\n\n            # Otherwise, resume the download from where it left off\n            headers = {'Range': f'bytes={output_path.stat().st_size}-'}\n            mode = 'ab'\n\n        with self.s.get(url, stream=True, headers=headers, timeout=20) as r:\n            r.raise_for_status()  # Do not continue the download if the request was unsuccessful\n            total_size = int(r.headers.get('content-length', 0))\n            block_size = 1024 * 1024  # 1MB\n            with open(output_path, mode) as f:\n                with tqdm.tqdm(total=total_size, unit='iB', unit_scale=True, bar_format='{l_bar}{bar}| {n_fmt:6}/{total_fmt:6} {rate_fmt:6}') as t:\n                    count = 0\n                    for data in r.iter_content(block_size):\n                        t.update(len(data))\n                        f.write(data)\n                        if total_size != 0 and self.progress_bar is not None:\n                            count += len(data)\n                            self.progress_bar(float(count) / float(total_size), f\"Downloading {filename}\")\n\n    def start_download_threads(self, file_list, output_folder, start_from_scratch=False, threads=1):\n        thread_map(lambda url: self.get_single_file(url, output_folder, start_from_scratch=start_from_scratch), file_list, max_workers=threads, disable=True)\n\n    def download_model_files(self, model, branch, links, sha256, output_folder, progress_bar=None, start_from_scratch=False, threads=1):\n        self.progress_bar = progress_bar\n\n        # Creating the folder and writing the metadata\n        output_folder.mkdir(parents=True, exist_ok=True)\n        metadata = f'url: https://huggingface.co/{model}\\n' \\\n                   f'branch: {branch}\\n' \\\n                   f'download date: {datetime.datetime.now().strftime(\"%Y-%m-%d %H:%M:%S\")}\\n'\n\n        sha256_str = '\\n'.join([f'    {item[1]} {item[0]}' for item in sha256])\n        if sha256_str:\n            metadata += f'sha256sum:\\n{sha256_str}'\n\n        metadata += '\\n'\n        (output_folder / 'huggingface-metadata.txt').write_text(metadata)\n\n        # Downloading the files\n        print(f\"Downloading the model to {output_folder}\")\n        self.start_download_threads(links, output_folder, start_from_scratch=start_from_scratch, threads=threads)\n\n    def check_model_files(self, model, branch, links, sha256, output_folder):\n        # Validate the checksums\n        validated = True\n        for i in range(len(sha256)):\n            fpath = (output_folder / sha256[i][0])\n\n            if not fpath.exists():\n                print(f\"The following file is missing: {fpath}\")\n                validated = False\n                continue\n\n            with open(output_folder / sha256[i][0], \"rb\") as f:\n                bytes = f.read()\n                file_hash = hashlib.sha256(bytes).hexdigest()\n                if file_hash != sha256[i][1]:\n                    print(f'Checksum failed: {sha256[i][0]}  {sha256[i][1]}')\n                    validated = False\n                else:\n                    print(f'Checksum validated: {sha256[i][0]}  {sha256[i][1]}')\n\n        if validated:\n            print('[+] Validated checksums of all model files!')\n        else:\n            print('[-] Invalid checksums. Rerun download-model.py with the --clean flag.')\n\n\nif __name__ == '__main__':\n\n    parser = argparse.ArgumentParser()\n    parser.add_argument('MODEL', type=str, default=None, nargs='?')\n    parser.add_argument('--branch', type=str, default='main', help='Name of the Git branch to download from.')\n    parser.add_argument('--threads', type=int, default=1, help='Number of files to download simultaneously.')\n    parser.add_argument('--text-only', action='store_true', help='Only download text files (txt/json).')\n    parser.add_argument('--output', type=str, default=None, help='The folder where the model should be saved.')\n    parser.add_argument('--clean', action='store_true', help='Does not resume the previous download.')\n    parser.add_argument('--check', action='store_true', help='Validates the checksums of model files.')\n    parser.add_argument('--max-retries', type=int, default=5, help='Max retries count when get error in download time.')\n    args = parser.parse_args()\n\n    branch = args.branch\n    model = args.MODEL\n\n    if model is None:\n        print(\"Error: Please specify the model you'd like to download (e.g. 'python download-model.py facebook/opt-1.3b').\")\n        sys.exit()\n\n    downloader = ModelDownloader(max_retries=args.max_retries)\n    # Cleaning up the model/branch names\n    try:\n        model, branch = downloader.sanitize_model_and_branch_names(model, branch)\n    except ValueError as err_branch:\n        print(f\"Error: {err_branch}\")\n        sys.exit()\n\n    # Getting the download links from Hugging Face\n    links, sha256, is_lora = downloader.get_download_links_from_huggingface(model, branch, text_only=args.text_only)\n\n    # Getting the output folder\n    output_folder = downloader.get_output_folder(model, branch, is_lora, base_folder=args.output)\n\n    if args.check:\n        # Check previously downloaded files\n        downloader.check_model_files(model, branch, links, sha256, output_folder)\n    else:\n        # Download files\n        downloader.download_model_files(model, branch, links, sha256, output_folder, threads=args.threads)\n", "path": "download-model.py"}]}