webscout 6.3__py3-none-any.whl → 6.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of webscout might be problematic. Click here for more details.

Files changed (85) hide show
  1. webscout/AIauto.py +191 -176
  2. webscout/AIbase.py +0 -197
  3. webscout/AIutel.py +488 -1130
  4. webscout/Bing_search.py +250 -153
  5. webscout/DWEBS.py +151 -19
  6. webscout/Extra/__init__.py +2 -1
  7. webscout/Extra/autocoder/__init__.py +9 -0
  8. webscout/Extra/autocoder/autocoder_utiles.py +121 -0
  9. webscout/Extra/autocoder/rawdog.py +681 -0
  10. webscout/Extra/autollama.py +246 -195
  11. webscout/Extra/gguf.py +441 -416
  12. webscout/LLM.py +206 -43
  13. webscout/Litlogger/__init__.py +681 -0
  14. webscout/Provider/DARKAI.py +1 -1
  15. webscout/Provider/EDITEE.py +1 -1
  16. webscout/Provider/NinjaChat.py +1 -1
  17. webscout/Provider/PI.py +221 -207
  18. webscout/Provider/Perplexity.py +598 -598
  19. webscout/Provider/RoboCoders.py +206 -0
  20. webscout/Provider/TTI/AiForce/__init__.py +22 -0
  21. webscout/Provider/TTI/AiForce/async_aiforce.py +257 -0
  22. webscout/Provider/TTI/AiForce/sync_aiforce.py +242 -0
  23. webscout/Provider/TTI/Nexra/__init__.py +22 -0
  24. webscout/Provider/TTI/Nexra/async_nexra.py +286 -0
  25. webscout/Provider/TTI/Nexra/sync_nexra.py +258 -0
  26. webscout/Provider/TTI/PollinationsAI/__init__.py +23 -0
  27. webscout/Provider/TTI/PollinationsAI/async_pollinations.py +330 -0
  28. webscout/Provider/TTI/PollinationsAI/sync_pollinations.py +285 -0
  29. webscout/Provider/TTI/__init__.py +2 -4
  30. webscout/Provider/TTI/artbit/__init__.py +22 -0
  31. webscout/Provider/TTI/artbit/async_artbit.py +184 -0
  32. webscout/Provider/TTI/artbit/sync_artbit.py +176 -0
  33. webscout/Provider/TTI/blackbox/__init__.py +4 -0
  34. webscout/Provider/TTI/blackbox/async_blackbox.py +212 -0
  35. webscout/Provider/TTI/{blackboximage.py → blackbox/sync_blackbox.py} +199 -153
  36. webscout/Provider/TTI/deepinfra/__init__.py +4 -0
  37. webscout/Provider/TTI/deepinfra/async_deepinfra.py +227 -0
  38. webscout/Provider/TTI/deepinfra/sync_deepinfra.py +199 -0
  39. webscout/Provider/TTI/huggingface/__init__.py +22 -0
  40. webscout/Provider/TTI/huggingface/async_huggingface.py +199 -0
  41. webscout/Provider/TTI/huggingface/sync_huggingface.py +195 -0
  42. webscout/Provider/TTI/imgninza/__init__.py +4 -0
  43. webscout/Provider/TTI/imgninza/async_ninza.py +214 -0
  44. webscout/Provider/TTI/{imgninza.py → imgninza/sync_ninza.py} +209 -136
  45. webscout/Provider/TTI/talkai/__init__.py +4 -0
  46. webscout/Provider/TTI/talkai/async_talkai.py +229 -0
  47. webscout/Provider/TTI/talkai/sync_talkai.py +207 -0
  48. webscout/Provider/__init__.py +146 -139
  49. webscout/Provider/askmyai.py +2 -2
  50. webscout/Provider/cerebras.py +227 -219
  51. webscout/Provider/llama3mitril.py +0 -1
  52. webscout/Provider/mhystical.py +176 -0
  53. webscout/Provider/perplexitylabs.py +265 -0
  54. webscout/Provider/twitterclone.py +251 -245
  55. webscout/Provider/typegpt.py +359 -0
  56. webscout/__init__.py +28 -23
  57. webscout/__main__.py +5 -5
  58. webscout/cli.py +252 -280
  59. webscout/conversation.py +227 -0
  60. webscout/exceptions.py +161 -29
  61. webscout/litagent/__init__.py +172 -0
  62. webscout/litprinter/__init__.py +831 -0
  63. webscout/optimizers.py +270 -0
  64. webscout/prompt_manager.py +279 -0
  65. webscout/swiftcli/__init__.py +810 -0
  66. webscout/transcriber.py +479 -551
  67. webscout/update_checker.py +125 -0
  68. webscout/version.py +1 -1
  69. {webscout-6.3.dist-info → webscout-6.4.dist-info}/METADATA +26 -45
  70. {webscout-6.3.dist-info → webscout-6.4.dist-info}/RECORD +75 -45
  71. webscout/Provider/TTI/AIuncensoredimage.py +0 -103
  72. webscout/Provider/TTI/Nexra.py +0 -120
  73. webscout/Provider/TTI/PollinationsAI.py +0 -138
  74. webscout/Provider/TTI/WebSimAI.py +0 -142
  75. webscout/Provider/TTI/aiforce.py +0 -160
  76. webscout/Provider/TTI/artbit.py +0 -141
  77. webscout/Provider/TTI/deepinfra.py +0 -148
  78. webscout/Provider/TTI/huggingface.py +0 -155
  79. webscout/Provider/TTI/talkai.py +0 -116
  80. webscout/models.py +0 -23
  81. /webscout/{g4f.py → gpt4free.py} +0 -0
  82. {webscout-6.3.dist-info → webscout-6.4.dist-info}/LICENSE.md +0 -0
  83. {webscout-6.3.dist-info → webscout-6.4.dist-info}/WHEEL +0 -0
  84. {webscout-6.3.dist-info → webscout-6.4.dist-info}/entry_points.txt +0 -0
  85. {webscout-6.3.dist-info → webscout-6.4.dist-info}/top_level.txt +0 -0
webscout/Extra/gguf.py CHANGED
@@ -1,416 +1,441 @@
1
- # webscout/Extra/gguf.py
2
- import subprocess
3
- import os
4
- import sys
5
- import logging
6
- import shutil
7
- from pathlib import Path
8
- from typing import List, Optional, Dict, Any
9
- from pyfiglet import figlet_format
10
- from rich.console import Console
11
- from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, TaskProgressColumn
12
- from rich.logging import RichHandler
13
- from rich.panel import Panel
14
- from rich.table import Table
15
-
16
- # Set up logging with Rich
17
- logging.basicConfig(
18
- level=logging.INFO,
19
- format="%(message)s",
20
- datefmt="[%X]",
21
- handlers=[RichHandler(rich_tracebacks=True)]
22
- )
23
-
24
- log = logging.getLogger("rich")
25
- console = Console()
26
-
27
- class ConversionError(Exception):
28
- """Custom exception for conversion errors"""
29
- pass
30
-
31
- class ModelConverter:
32
- """Handles the conversion of Hugging Face models to GGUF format."""
33
-
34
- VALID_METHODS = {
35
- "q2_k": "2-bit quantization",
36
- "q3_k_l": "3-bit quantization (large)",
37
- "q3_k_m": "3-bit quantization (medium)",
38
- "q3_k_s": "3-bit quantization (small)",
39
- "q4_0": "4-bit quantization (version 0)",
40
- "q4_1": "4-bit quantization (version 1)",
41
- "q4_k_m": "4-bit quantization (medium)",
42
- "q4_k_s": "4-bit quantization (small)",
43
- "q5_0": "5-bit quantization (version 0)",
44
- "q5_1": "5-bit quantization (version 1)",
45
- "q5_k_m": "5-bit quantization (medium)",
46
- "q5_k_s": "5-bit quantization (small)",
47
- "q6_k": "6-bit quantization",
48
- "q8_0": "8-bit quantization"
49
- }
50
-
51
- def __init__(self, model_id: str, username: Optional[str] = None,
52
- token: Optional[str] = None, quantization_methods: str = "q4_k_m,q5_k_m"):
53
- self.model_id = model_id
54
- self.username = username
55
- self.token = token
56
- self.quantization_methods = quantization_methods.split(',')
57
- self.model_name = model_id.split('/')[-1]
58
- self.workspace = Path(os.getcwd())
59
-
60
- def validate_inputs(self) -> None:
61
- """Validates all input parameters."""
62
- if not '/' in self.model_id:
63
- raise ValueError("Invalid model ID format. Expected format: 'organization/model-name'")
64
-
65
- invalid_methods = [m for m in self.quantization_methods if m not in self.VALID_METHODS]
66
- if invalid_methods:
67
- raise ValueError(
68
- f"Invalid quantization methods: {', '.join(invalid_methods)}.\n"
69
- f"Valid methods are: {', '.join(self.VALID_METHODS.keys())}"
70
- )
71
-
72
- if bool(self.username) != bool(self.token):
73
- raise ValueError("Both username and token must be provided for upload, or neither.")
74
-
75
- @staticmethod
76
- def check_dependencies() -> Dict[str, bool]:
77
- """Check if all required dependencies are installed."""
78
- dependencies = {
79
- 'git': 'Git version control',
80
- 'pip3': 'Python package installer',
81
- 'huggingface-cli': 'Hugging Face CLI',
82
- 'nvcc': 'NVIDIA CUDA Compiler (optional)'
83
- }
84
-
85
- status = {}
86
- for cmd, desc in dependencies.items():
87
- status[cmd] = subprocess.run(['which', cmd], capture_output=True, text=True).returncode == 0
88
-
89
- return status
90
-
91
- def setup_llama_cpp(self) -> None:
92
- """Sets up and builds llama.cpp repository."""
93
- llama_path = self.workspace / "llama.cpp"
94
-
95
- with console.status("[bold green]Setting up llama.cpp...") as status:
96
- if not llama_path.exists():
97
- log.info("Cloning llama.cpp repository...")
98
- subprocess.run(['git', 'clone', 'https://github.com/ggerganov/llama.cpp'], check=True)
99
-
100
- os.chdir(llama_path)
101
- log.info("Installing requirements...")
102
- subprocess.run(['pip3', 'install', '-r', 'requirements.txt'], check=True)
103
-
104
- has_cuda = subprocess.run(['nvcc', '--version'], capture_output=True).returncode == 0
105
-
106
- log.info("Building llama.cpp...")
107
- subprocess.run(['make', 'clean'], check=True)
108
- if has_cuda:
109
- status.update("[bold green]Building with CUDA support...")
110
- subprocess.run(['make', 'LLAMA_CUBLAS=1'], check=True)
111
- else:
112
- status.update("[bold yellow]Building without CUDA support...")
113
- subprocess.run(['make'], check=True)
114
-
115
- os.chdir(self.workspace)
116
-
117
- def display_config(self) -> None:
118
- """Displays the current configuration in a formatted table."""
119
- table = Table(title="Configuration", show_header=True, header_style="bold magenta")
120
- table.add_column("Setting", style="cyan")
121
- table.add_column("Value", style="green")
122
-
123
- table.add_row("Model ID", self.model_id)
124
- table.add_row("Model Name", self.model_name)
125
- table.add_row("Username", self.username or "Not provided")
126
- table.add_row("Token", "****" if self.token else "Not provided")
127
- table.add_row("Quantization Methods", "\n".join(
128
- f"{method} ({self.VALID_METHODS[method]})"
129
- for method in self.quantization_methods
130
- ))
131
-
132
- console.print(Panel(table))
133
-
134
- def convert(self) -> None:
135
- """Performs the model conversion process."""
136
- try:
137
- # Display banner and configuration
138
- console.print(f"[bold green]{figlet_format('GGUF Converter')}[/]\n", justify="center")
139
- self.display_config()
140
-
141
- # Validate inputs
142
- self.validate_inputs()
143
-
144
- # Check dependencies
145
- deps = self.check_dependencies()
146
- missing = [name for name, installed in deps.items() if not installed and name != 'nvcc']
147
- if missing:
148
- raise ConversionError(f"Missing required dependencies: {', '.join(missing)}")
149
-
150
- # Setup llama.cpp
151
- self.setup_llama_cpp()
152
-
153
- # Create and execute conversion script
154
- script_path = self.workspace / "gguf.sh"
155
- if not script_path.exists():
156
- self._create_conversion_script(script_path)
157
-
158
- # Prepare command
159
- command = ["bash", str(script_path), "-m", self.model_id]
160
- if self.username and self.token:
161
- command.extend(["-u", self.username, "-t", self.token])
162
- command.extend(["-q", ",".join(self.quantization_methods)])
163
-
164
- # Execute conversion with progress tracking
165
- with Progress(
166
- SpinnerColumn(),
167
- TextColumn("[progress.description]{task.description}"),
168
- BarColumn(),
169
- TaskProgressColumn(),
170
- console=console
171
- ) as progress:
172
- task = progress.add_task("Converting model...", total=None)
173
-
174
- process = subprocess.Popen(
175
- command,
176
- stdout=subprocess.PIPE,
177
- stderr=subprocess.PIPE,
178
- text=True,
179
- bufsize=1,
180
- universal_newlines=True
181
- )
182
-
183
- while True:
184
- output = process.stdout.readline()
185
- if output == '' and process.poll() is not None:
186
- break
187
- if output:
188
- progress.update(task, description=output.strip())
189
- log.info(output.strip())
190
-
191
- stderr = process.stderr.read()
192
- if stderr:
193
- log.warning(stderr)
194
-
195
- if process.returncode != 0:
196
- raise ConversionError(f"Conversion failed with return code {process.returncode}")
197
-
198
- progress.update(task, completed=True)
199
-
200
- # Display success message
201
- console.print(Panel.fit(
202
- "[bold green]✓[/] Conversion completed successfully!\n\n"
203
- f"[cyan]Output files can be found in: {self.workspace / self.model_name}[/]",
204
- title="Success",
205
- border_style="green"
206
- ))
207
-
208
- except Exception as e:
209
- console.print(Panel.fit(
210
- f"[bold red]✗[/] {str(e)}",
211
- title="Error",
212
- border_style="red"
213
- ))
214
- raise
215
-
216
- def _create_conversion_script(self, script_path: Path) -> None:
217
- """Creates the conversion shell script."""
218
- script_content = """cat << "EOF"
219
- Made with love in India
220
- EOF
221
-
222
- # Default values
223
- MODEL_ID=""
224
- USERNAME=""
225
- TOKEN=""
226
- QUANTIZATION_METHODS="q4_k_m,q5_k_m" # Default to "q4_k_m,q5_k_m" if not provided
227
-
228
- # Display help/usage information
229
- usage() {
230
- echo "Usage: $0 -m MODEL_ID [-u USERNAME] [-t TOKEN] [-q QUANTIZATION_METHODS]"
231
- echo
232
- echo "Options:"
233
- echo " -m MODEL_ID Required: Set the HF model ID"
234
- echo " -u USERNAME Optional: Set the username"
235
- echo " -t TOKEN Optional: Set the token"
236
- echo " -q QUANTIZATION_METHODS Optional: Set the quantization methods (default: q4_k_m,q5_k_m)"
237
- echo " -h Display this help and exit"
238
- echo
239
- }
240
-
241
- # Parse command-line options
242
- while getopts ":m:u:t:q:h" opt; do
243
- case ${opt} in
244
- m )
245
- MODEL_ID=$OPTARG
246
- ;;
247
- u )
248
- USERNAME=$OPTARG
249
- ;;
250
- t )
251
- TOKEN=$OPTARG
252
- ;;
253
- q )
254
- QUANTIZATION_METHODS=$OPTARG
255
- ;;
256
- h )
257
- usage
258
- exit 0
259
- ;;
260
- \? )
261
- echo "Invalid Option: -$OPTARG" 1>&2
262
- usage
263
- exit 1
264
- ;;
265
- : )
266
- echo "Invalid Option: -$OPTARG requires an argument" 1>&2
267
- usage
268
- exit 1
269
- ;;
270
- esac
271
- done
272
- shift $((OPTIND -1))
273
-
274
- # Ensure MODEL_ID is provided
275
- if [ -z "$MODEL_ID" ]; then
276
- echo "Error: MODEL_ID is required."
277
- usage
278
- exit 1
279
- fi
280
-
281
- # # Echoing the arguments for checking
282
- # echo "MODEL_ID: $MODEL_ID"
283
- # echo "USERNAME: ${USERNAME:-'Not provided'}"
284
- # echo "TOKEN: ${TOKEN:-'Not provided'}"
285
- # echo "QUANTIZATION_METHODS: $QUANTIZATION_METHODS"
286
-
287
- # Splitting string into an array for quantization methods, if provided
288
- IFS=',' read -r -a QUANTIZATION_METHOD_ARRAY <<< "$QUANTIZATION_METHODS"
289
- echo "Quantization Methods: ${QUANTIZATION_METHOD_ARRAY[@]}"
290
-
291
- MODEL_NAME=$(echo "$MODEL_ID" | awk -F'/' '{print $NF}')
292
-
293
-
294
- # ----------- llama.cpp setup block-----------
295
- # Check if llama.cpp is already installed and skip the build step if it is
296
- if [ ! -d "llama.cpp" ]; then
297
- echo "llama.cpp not found. Cloning and setting up..."
298
- git clone https://github.com/ggerganov/llama.cpp
299
- cd llama.cpp && git pull
300
- # Install required packages
301
- pip3 install -r requirements.txt
302
- # Build llama.cpp as it's freshly cloned
303
- if ! command -v nvcc &> /dev/null
304
- then
305
- echo "nvcc could not be found, building llama without LLAMA_CUBLAS"
306
- make clean && make
307
- else
308
- make clean && LLAMA_CUBLAS=1 make
309
- fi
310
- cd ..
311
- else
312
- echo "llama.cpp found. Assuming it's already built and up to date."
313
- # Optionally, still update dependencies
314
- # cd llama.cpp && pip3 install -r requirements.txt && cd ..
315
- fi
316
- # ----------- llama.cpp setup block-----------
317
-
318
-
319
-
320
- # Download model
321
- #todo : shall we put condition to check if model has been already downloaded? similar to autogguf?
322
- echo "Downloading the model..."
323
- huggingface-cli download "$MODEL_ID" --local-dir "./${MODEL_NAME}" --local-dir-use-symlinks False --revision main
324
-
325
-
326
- # Convert to fp16
327
- FP16="${MODEL_NAME}/${MODEL_NAME,,}.fp16.bin"
328
- echo "Converting the model to fp16..."
329
- python3 llama.cpp/convert_hf_to_gguf.py "$MODEL_NAME" --outtype f16 --outfile "$FP16"
330
-
331
- # Quantize the model
332
- echo "Quantizing the model..."
333
- for METHOD in "${QUANTIZATION_METHOD_ARRAY[@]}"; do
334
- QTYPE="${MODEL_NAME}/${MODEL_NAME,,}.${METHOD^^}.gguf"
335
- ./llama.cpp/llama-quantize "$FP16" "$QTYPE" "$METHOD"
336
- done
337
-
338
-
339
- # Check if USERNAME and TOKEN are provided
340
- if [[ -n "$USERNAME" && -n "$TOKEN" ]]; then
341
-
342
- # Login to Hugging Face
343
- echo "Logging in to Hugging Face..."
344
- huggingface-cli login --token "$TOKEN"
345
-
346
-
347
- # Uploading .gguf, .md files, and config.json
348
- echo "Uploading .gguf, .md files, and config.json..."
349
-
350
-
351
- # Define a temporary directory
352
- TEMP_DIR="./temp_upload_dir"
353
-
354
- # Create the temporary directory
355
- mkdir -p "${TEMP_DIR}"
356
-
357
- # Copy the specific files to the temporary directory
358
- find "./${MODEL_NAME}" -type f \( -name "*.gguf" -o -name "*.md" -o -name "config.json" \) -exec cp {} "${TEMP_DIR}/" \;
359
-
360
- # Upload the temporary directory to Hugging Face
361
- huggingface-cli upload "${USERNAME}/${MODEL_NAME}-GGUF" "${TEMP_DIR}" --private
362
-
363
- # Remove the temporary directory after upload
364
- rm -rf "${TEMP_DIR}"
365
- echo "Upload completed."
366
- else
367
- echo "USERNAME and TOKEN must be provided for upload."
368
- fi
369
-
370
- echo "Script completed."
371
- """
372
- script_path.write_text(script_content)
373
- script_path.chmod(0o755)
374
-
375
- def convert(
376
- model_id: str,
377
- username: Optional[str] = None,
378
- token: Optional[str] = None,
379
- quantization_methods: str = "q4_k_m,q5_k_m"
380
- ) -> None:
381
- """Converts and quantizes a Hugging Face model to GGUF format.
382
-
383
- Args:
384
- model_id (str): The Hugging Face model ID (e.g., 'google/flan-t5-xl').
385
- username (str, optional): Your Hugging Face username. Required for uploads.
386
- token (str, optional): Your Hugging Face API token. Required for uploads.
387
- quantization_methods (str, optional): Comma-separated quantization methods.
388
- Defaults to "q4_k_m,q5_k_m".
389
-
390
- Raises:
391
- ConversionError: If any step in the conversion process fails.
392
- ValueError: If invalid parameters are provided.
393
- """
394
- converter = ModelConverter(model_id, username, token, quantization_methods)
395
- converter.convert()
396
-
397
- if __name__ == "__main__":
398
- import argparse
399
-
400
- parser = argparse.ArgumentParser(description="Convert Hugging Face models to GGUF format")
401
- parser.add_argument("model_id", help="The Hugging Face model ID (e.g., 'google/flan-t5-xl')")
402
- parser.add_argument("-u", "--username", help="Your Hugging Face username")
403
- parser.add_argument("-t", "--token", help="Your Hugging Face API token")
404
- parser.add_argument(
405
- "-q", "--quantization-methods",
406
- default="q4_k_m,q5_k_m",
407
- help="Comma-separated quantization methods"
408
- )
409
-
410
- args = parser.parse_args()
411
- convert(
412
- model_id=args.model_id,
413
- username=args.username,
414
- token=args.token,
415
- quantization_methods=args.quantization_methods
416
- )
1
+ """
2
+ Yo fam! 🔥 Welcome to GGUF Converter - your ultimate tool for converting models to GGUF format! 💪
3
+
4
+ - Converting HuggingFace models to GGUF format 🚀
5
+ - Multiple quantization methods for different needs 🎯
6
+ - Easy upload back to HuggingFace Hub 📤
7
+
8
+ Usage:
9
+ >>> python -m webscout.Extra.gguf convert -m "OEvortex/HelpingAI-Lite-1.5T" -q "q4_k_m,q5_k_m"
10
+ >>> # With upload options:
11
+ >>> python -m webscout.Extra.gguf convert -m "your-model" -u "username" -t "token" -q "q4_k_m"
12
+
13
+ Features:
14
+ - Smart dependency checking 🔍
15
+ - CUDA support detection ⚡
16
+ - Progress tracking that keeps it real 📈
17
+ - Multiple quantization options 🎮
18
+
19
+ Join the squad on Discord and level up your AI game! 🎮
20
+ """
21
+
22
+ import subprocess
23
+ import os
24
+ import sys
25
+ import shutil
26
+ from pathlib import Path
27
+ from typing import List, Optional, Dict, Any
28
+ from pyfiglet import figlet_format
29
+ from rich.console import Console
30
+ from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, TaskProgressColumn
31
+ from rich.panel import Panel
32
+ from rich.table import Table
33
+ from ..Litlogger import LitLogger, LogFormat, ColorScheme
34
+ from ..swiftcli import CLI, option
35
+
36
+ # Initialize LitLogger with ocean vibes
37
+ logger = LitLogger(
38
+ name="GGUFConverter",
39
+ format=LogFormat.MODERN_EMOJI,
40
+ color_scheme=ColorScheme.OCEAN
41
+ )
42
+
43
+ console = Console()
44
+
45
+ class ConversionError(Exception):
46
+ """Custom exception for when things don't go as planned! ⚠️"""
47
+ pass
48
+
49
+ class ModelConverter:
50
+ """Handles the conversion of Hugging Face models to GGUF format."""
51
+
52
+ VALID_METHODS = {
53
+ "q2_k": "2-bit quantization",
54
+ "q3_k_l": "3-bit quantization (large)",
55
+ "q3_k_m": "3-bit quantization (medium)",
56
+ "q3_k_s": "3-bit quantization (small)",
57
+ "q4_0": "4-bit quantization (version 0)",
58
+ "q4_1": "4-bit quantization (version 1)",
59
+ "q4_k_m": "4-bit quantization (medium)",
60
+ "q4_k_s": "4-bit quantization (small)",
61
+ "q5_0": "5-bit quantization (version 0)",
62
+ "q5_1": "5-bit quantization (version 1)",
63
+ "q5_k_m": "5-bit quantization (medium)",
64
+ "q5_k_s": "5-bit quantization (small)",
65
+ "q6_k": "6-bit quantization",
66
+ "q8_0": "8-bit quantization"
67
+ }
68
+
69
+ def __init__(self, model_id: str, username: Optional[str] = None,
70
+ token: Optional[str] = None, quantization_methods: str = "q4_k_m,q5_k_m"):
71
+ self.model_id = model_id
72
+ self.username = username
73
+ self.token = token
74
+ self.quantization_methods = quantization_methods.split(',')
75
+ self.model_name = model_id.split('/')[-1]
76
+ self.workspace = Path(os.getcwd())
77
+
78
+ def validate_inputs(self) -> None:
79
+ """Validates all input parameters."""
80
+ if not '/' in self.model_id:
81
+ raise ValueError("Invalid model ID format. Expected format: 'organization/model-name'")
82
+
83
+ invalid_methods = [m for m in self.quantization_methods if m not in self.VALID_METHODS]
84
+ if invalid_methods:
85
+ raise ValueError(
86
+ f"Invalid quantization methods: {', '.join(invalid_methods)}.\n"
87
+ f"Valid methods are: {', '.join(self.VALID_METHODS.keys())}"
88
+ )
89
+
90
+ if bool(self.username) != bool(self.token):
91
+ raise ValueError("Both username and token must be provided for upload, or neither.")
92
+
93
+ @staticmethod
94
+ def check_dependencies() -> Dict[str, bool]:
95
+ """Check if all required dependencies are installed."""
96
+ dependencies = {
97
+ 'git': 'Git version control',
98
+ 'pip3': 'Python package installer',
99
+ 'huggingface-cli': 'Hugging Face CLI',
100
+ 'nvcc': 'NVIDIA CUDA Compiler (optional)'
101
+ }
102
+
103
+ status = {}
104
+ for cmd, desc in dependencies.items():
105
+ status[cmd] = subprocess.run(['which', cmd], capture_output=True, text=True).returncode == 0
106
+
107
+ return status
108
+
109
+ def setup_llama_cpp(self) -> None:
110
+ """Sets up and builds llama.cpp repository."""
111
+ llama_path = self.workspace / "llama.cpp"
112
+
113
+ with console.status("[bold green]Setting up llama.cpp...") as status:
114
+ if not llama_path.exists():
115
+ logger.info("Cloning llama.cpp repository...")
116
+ subprocess.run(['git', 'clone', 'https://github.com/ggerganov/llama.cpp'], check=True)
117
+
118
+ os.chdir(llama_path)
119
+ logger.info("Installing requirements...")
120
+ subprocess.run(['pip3', 'install', '-r', 'requirements.txt'], check=True)
121
+
122
+ has_cuda = subprocess.run(['nvcc', '--version'], capture_output=True).returncode == 0
123
+
124
+ logger.info("Building llama.cpp...")
125
+ subprocess.run(['make', 'clean'], check=True)
126
+ if has_cuda:
127
+ status.update("[bold green]Building with CUDA support...")
128
+ subprocess.run(['make', 'LLAMA_CUBLAS=1'], check=True)
129
+ else:
130
+ status.update("[bold yellow]Building without CUDA support...")
131
+ subprocess.run(['make'], check=True)
132
+
133
+ os.chdir(self.workspace)
134
+
135
+ def display_config(self) -> None:
136
+ """Displays the current configuration in a formatted table."""
137
+ table = Table(title="Configuration", show_header=True, header_style="bold magenta")
138
+ table.add_column("Setting", style="cyan")
139
+ table.add_column("Value", style="green")
140
+
141
+ table.add_row("Model ID", self.model_id)
142
+ table.add_row("Model Name", self.model_name)
143
+ table.add_row("Username", self.username or "Not provided")
144
+ table.add_row("Token", "****" if self.token else "Not provided")
145
+ table.add_row("Quantization Methods", "\n".join(
146
+ f"{method} ({self.VALID_METHODS[method]})"
147
+ for method in self.quantization_methods
148
+ ))
149
+
150
+ console.print(Panel(table))
151
+
152
+ def convert(self) -> None:
153
+ """Performs the model conversion process."""
154
+ try:
155
+ # Display banner and configuration
156
+ console.print(f"[bold green]{figlet_format('GGUF Converter')}[/]\n", justify="center")
157
+ self.display_config()
158
+
159
+ # Validate inputs
160
+ self.validate_inputs()
161
+
162
+ # Check dependencies
163
+ deps = self.check_dependencies()
164
+ missing = [name for name, installed in deps.items() if not installed and name != 'nvcc']
165
+ if missing:
166
+ raise ConversionError(f"Missing required dependencies: {', '.join(missing)}")
167
+
168
+ # Setup llama.cpp
169
+ self.setup_llama_cpp()
170
+
171
+ # Create and execute conversion script
172
+ script_path = self.workspace / "gguf.sh"
173
+ if not script_path.exists():
174
+ self._create_conversion_script(script_path)
175
+
176
+ # Prepare command
177
+ command = ["bash", str(script_path), "-m", self.model_id]
178
+ if self.username and self.token:
179
+ command.extend(["-u", self.username, "-t", self.token])
180
+ command.extend(["-q", ",".join(self.quantization_methods)])
181
+
182
+ # Execute conversion with progress tracking
183
+ with Progress(
184
+ SpinnerColumn(),
185
+ TextColumn("[progress.description]{task.description}"),
186
+ BarColumn(),
187
+ TaskProgressColumn(),
188
+ console=console
189
+ ) as progress:
190
+ task = progress.add_task("Converting model...", total=None)
191
+
192
+ process = subprocess.Popen(
193
+ command,
194
+ stdout=subprocess.PIPE,
195
+ stderr=subprocess.PIPE,
196
+ text=True,
197
+ bufsize=1,
198
+ universal_newlines=True
199
+ )
200
+
201
+ while True:
202
+ output = process.stdout.readline()
203
+ if output == '' and process.poll() is not None:
204
+ break
205
+ if output:
206
+ progress.update(task, description=output.strip())
207
+ logger.info(output.strip())
208
+
209
+ stderr = process.stderr.read()
210
+ if stderr:
211
+ logger.warning(stderr)
212
+
213
+ if process.returncode != 0:
214
+ raise ConversionError(f"Conversion failed with return code {process.returncode}")
215
+
216
+ progress.update(task, completed=True)
217
+
218
+ # Display success message
219
+ console.print(Panel.fit(
220
+ "[bold green]✓[/] Conversion completed successfully!\n\n"
221
+ f"[cyan]Output files can be found in: {self.workspace / self.model_name}[/]",
222
+ title="Success",
223
+ border_style="green"
224
+ ))
225
+
226
+ except Exception as e:
227
+ console.print(Panel.fit(
228
+ f"[bold red]✗[/] {str(e)}",
229
+ title="Error",
230
+ border_style="red"
231
+ ))
232
+ raise
233
+
234
+ def _create_conversion_script(self, script_path: Path) -> None:
235
+ """Creates the conversion shell script."""
236
+ script_content = """cat << "EOF"
237
+ Made with love in India
238
+ EOF
239
+
240
+ # Default values
241
+ MODEL_ID=""
242
+ USERNAME=""
243
+ TOKEN=""
244
+ QUANTIZATION_METHODS="q4_k_m,q5_k_m" # Default to "q4_k_m,q5_k_m" if not provided
245
+
246
+ # Display help/usage information
247
+ usage() {
248
+ echo "Usage: $0 -m MODEL_ID [-u USERNAME] [-t TOKEN] [-q QUANTIZATION_METHODS]"
249
+ echo
250
+ echo "Options:"
251
+ echo " -m MODEL_ID Required: Set the HF model ID"
252
+ echo " -u USERNAME Optional: Set the username"
253
+ echo " -t TOKEN Optional: Set the token"
254
+ echo " -q QUANTIZATION_METHODS Optional: Set the quantization methods (default: q4_k_m,q5_k_m)"
255
+ echo " -h Display this help and exit"
256
+ echo
257
+ }
258
+
259
+ # Parse command-line options
260
+ while getopts ":m:u:t:q:h" opt; do
261
+ case ${opt} in
262
+ m )
263
+ MODEL_ID=$OPTARG
264
+ ;;
265
+ u )
266
+ USERNAME=$OPTARG
267
+ ;;
268
+ t )
269
+ TOKEN=$OPTARG
270
+ ;;
271
+ q )
272
+ QUANTIZATION_METHODS=$OPTARG
273
+ ;;
274
+ h )
275
+ usage
276
+ exit 0
277
+ ;;
278
+ \? )
279
+ echo "Invalid Option: -$OPTARG" 1>&2
280
+ usage
281
+ exit 1
282
+ ;;
283
+ : )
284
+ echo "Invalid Option: -$OPTARG requires an argument" 1>&2
285
+ usage
286
+ exit 1
287
+ ;;
288
+ esac
289
+ done
290
+ shift $((OPTIND -1))
291
+
292
+ # Ensure MODEL_ID is provided
293
+ if [ -z "$MODEL_ID" ]; then
294
+ echo "Error: MODEL_ID is required."
295
+ usage
296
+ exit 1
297
+ fi
298
+
299
+ # # Echoing the arguments for checking
300
+ # echo "MODEL_ID: $MODEL_ID"
301
+ # echo "USERNAME: ${USERNAME:-'Not provided'}"
302
+ # echo "TOKEN: ${TOKEN:-'Not provided'}"
303
+ # echo "QUANTIZATION_METHODS: $QUANTIZATION_METHODS"
304
+
305
+ # Splitting string into an array for quantization methods, if provided
306
+ IFS=',' read -r -a QUANTIZATION_METHOD_ARRAY <<< "$QUANTIZATION_METHODS"
307
+ echo "Quantization Methods: ${QUANTIZATION_METHOD_ARRAY[@]}"
308
+
309
+ MODEL_NAME=$(echo "$MODEL_ID" | awk -F'/' '{print $NF}')
310
+
311
+
312
+ # ----------- llama.cpp setup block-----------
313
+ # Check if llama.cpp is already installed and skip the build step if it is
314
+ if [ ! -d "llama.cpp" ]; then
315
+ echo "llama.cpp not found. Cloning and setting up..."
316
+ git clone https://github.com/ggerganov/llama.cpp
317
+ cd llama.cpp && git pull
318
+ # Install required packages
319
+ pip3 install -r requirements.txt
320
+ # Build llama.cpp as it's freshly cloned
321
+ if ! command -v nvcc &> /dev/null
322
+ then
323
+ echo "nvcc could not be found, building llama without LLAMA_CUBLAS"
324
+ make clean && make
325
+ else
326
+ make clean && LLAMA_CUBLAS=1 make
327
+ fi
328
+ cd ..
329
+ else
330
+ echo "llama.cpp found. Assuming it's already built and up to date."
331
+ # Optionally, still update dependencies
332
+ # cd llama.cpp && pip3 install -r requirements.txt && cd ..
333
+ fi
334
+ # ----------- llama.cpp setup block-----------
335
+
336
+
337
+
338
+ # Download model
339
+ #todo : shall we put condition to check if model has been already downloaded? similar to autogguf?
340
+ echo "Downloading the model..."
341
+ huggingface-cli download "$MODEL_ID" --local-dir "./${MODEL_NAME}" --local-dir-use-symlinks False --revision main
342
+
343
+
344
+ # Convert to fp16
345
+ FP16="${MODEL_NAME}/${MODEL_NAME,,}.fp16.bin"
346
+ echo "Converting the model to fp16..."
347
+ python3 llama.cpp/convert_hf_to_gguf.py "$MODEL_NAME" --outtype f16 --outfile "$FP16"
348
+
349
+ # Quantize the model
350
+ echo "Quantizing the model..."
351
+ for METHOD in "${QUANTIZATION_METHOD_ARRAY[@]}"; do
352
+ QTYPE="${MODEL_NAME}/${MODEL_NAME,,}.${METHOD^^}.gguf"
353
+ ./llama.cpp/llama-quantize "$FP16" "$QTYPE" "$METHOD"
354
+ done
355
+
356
+
357
+ # Check if USERNAME and TOKEN are provided
358
+ if [[ -n "$USERNAME" && -n "$TOKEN" ]]; then
359
+
360
+ # Login to Hugging Face
361
+ echo "Logging in to Hugging Face..."
362
+ huggingface-cli login --token "$TOKEN"
363
+
364
+
365
+ # Uploading .gguf, .md files, and config.json
366
+ echo "Uploading .gguf, .md files, and config.json..."
367
+
368
+
369
+ # Define a temporary directory
370
+ TEMP_DIR="./temp_upload_dir"
371
+
372
+ # Create the temporary directory
373
+ mkdir -p "${TEMP_DIR}"
374
+
375
+ # Copy the specific files to the temporary directory
376
+ find "./${MODEL_NAME}" -type f \( -name "*.gguf" -o -name "*.md" -o -name "config.json" \) -exec cp {} "${TEMP_DIR}/" \;
377
+
378
+ # Upload the temporary directory to Hugging Face
379
+ huggingface-cli upload "${USERNAME}/${MODEL_NAME}-GGUF" "${TEMP_DIR}" --private
380
+
381
+ # Remove the temporary directory after upload
382
+ rm -rf "${TEMP_DIR}"
383
+ echo "Upload completed."
384
+ else
385
+ echo "USERNAME and TOKEN must be provided for upload."
386
+ fi
387
+
388
+ echo "Script completed."
389
+ """
390
+ script_path.write_text(script_content)
391
+ script_path.chmod(0o755)
392
+
393
+ # Initialize CLI with HAI vibes
394
+ app = CLI(
395
+ name="gguf",
396
+ help="Convert HuggingFace models to GGUF format with style! 🔥",
397
+ version="1.0.0"
398
+ )
399
+
400
+ @app.command(name="convert")
401
+ @option("-m", "--model-id", help="The HuggingFace model ID (e.g., 'OEvortex/HelpingAI-Lite-1.5T')", required=True)
402
+ @option("-u", "--username", help="Your HuggingFace username for uploads", default=None)
403
+ @option("-t", "--token", help="Your HuggingFace API token for uploads", default=None)
404
+ @option("-q", "--quantization", help="Comma-separated quantization methods", default="q4_k_m,q5_k_m")
405
+ def convert_command(model_id: str, username: Optional[str] = None,
406
+ token: Optional[str] = None, quantization: str = "q4_k_m,q5_k_m"):
407
+ """
408
+ Convert and quantize HuggingFace models to GGUF format! 🚀
409
+
410
+ Args:
411
+ model_id (str): Your model's HF ID (like 'OEvortex/HelpingAI-Lite-1.5T') 🎯
412
+ username (str, optional): Your HF username for uploads 👤
413
+ token (str, optional): Your HF API token 🔑
414
+ quantization (str): Quantization methods (default: q4_k_m,q5_k_m) 🎮
415
+
416
+ Example:
417
+ >>> python -m webscout.Extra.gguf convert \\
418
+ ... -m "OEvortex/HelpingAI-Lite-1.5T" \\
419
+ ... -q "q4_k_m,q5_k_m"
420
+ """
421
+ try:
422
+ converter = ModelConverter(
423
+ model_id=model_id,
424
+ username=username,
425
+ token=token,
426
+ quantization_methods=quantization
427
+ )
428
+ converter.convert()
429
+ except (ConversionError, ValueError) as e:
430
+ logger.error(f"Conversion failed: {str(e)}")
431
+ sys.exit(1)
432
+ except Exception as e:
433
+ logger.error(f"Unexpected error: {str(e)}")
434
+ sys.exit(1)
435
+
436
+ def main():
437
+ """Fire up the GGUF converter! 🚀"""
438
+ app.run()
439
+
440
+ if __name__ == "__main__":
441
+ main()