webscout 6.2b0__py3-none-any.whl → 6.3__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.

webscout/Extra/gguf.py CHANGED
@@ -1,47 +1,221 @@
1
1
  # webscout/Extra/gguf.py
2
2
  import subprocess
3
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
4
9
  from pyfiglet import figlet_format
5
10
  from rich.console import Console
6
-
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")
7
25
  console = Console()
8
26
 
9
- def convert(model_id, username=None, token=None, quantization_methods="q4_k_m,q5_k_m"):
10
- """Converts and quantizes a Hugging Face model to GGUF format.
11
-
12
- Args:
13
- model_id (str): The Hugging Face model ID (e.g., 'google/flan-t5-xl').
14
- username (str, optional): Your Hugging Face username. Required for uploads.
15
- token (str, optional): Your Hugging Face API token. Required for uploads.
16
- quantization_methods (str, optional): Comma-separated quantization methods.
17
- Defaults to "q4_k_m,q5_k_m".
18
-
19
- Raises:
20
- ValueError: If an invalid quantization method is provided.
21
- """
27
+ class ConversionError(Exception):
28
+ """Custom exception for conversion errors"""
29
+ pass
22
30
 
23
- console.print(f"[bold green]{figlet_format('GGUF Converter')}[/]\n", justify="center")
24
- # List of valid quantization methods
25
- valid_methods = [
26
- "q2_k", "q3_k_l", "q3_k_m", "q3_k_s",
27
- "q4_0", "q4_1", "q4_k_m", "q4_k_s",
28
- "q5_0", "q5_1", "q5_k_m", "q5_k_s",
29
- "q6_k", "q8_0"
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))
31
133
 
32
- # Validate the selected quantization methods
33
- selected_methods_list = quantization_methods.split(',')
34
- for method in selected_methods_list:
35
- if method not in valid_methods:
36
- raise ValueError(f"Invalid method: {method}. Please select from the available methods: {', '.join(valid_methods)}")
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
37
215
 
38
- # Construct the absolute path to the shell script
39
- script_path = os.path.join(os.getcwd(), "gguf.sh")
40
- if not os.path.exists(script_path):
41
- # Create autollama.sh with the content provided
42
- with open(script_path, "w") as f:
43
- f.write("""
44
- cat << "EOF"
216
+ def _create_conversion_script(self, script_path: Path) -> None:
217
+ """Creates the conversion shell script."""
218
+ script_content = """cat << "EOF"
45
219
  Made with love in India
46
220
  EOF
47
221
 
@@ -143,7 +317,6 @@ fi
143
317
 
144
318
 
145
319
 
146
-
147
320
  # Download model
148
321
  #todo : shall we put condition to check if model has been already downloaded? similar to autogguf?
149
322
  echo "Downloading the model..."
@@ -195,32 +368,49 @@ else
195
368
  fi
196
369
 
197
370
  echo "Script completed."
198
- """)
199
- # Make autollama.sh executable (using chmod)
200
- os.chmod(script_path, 0o755)
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.
201
382
 
202
- # Construct the command
203
- command = ["bash", script_path, "-m", model_id]
204
-
205
- if username:
206
- command.extend(["-u", username])
207
-
208
- if token:
209
- command.extend(["-t", token])
210
-
211
- if quantization_methods:
212
- command.extend(["-q", quantization_methods])
213
-
214
- # Execute the command
215
- process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
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".
216
389
 
217
- # Print the output and error in real-time
218
- for line in process.stdout:
219
- print(line, end='')
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()
220
396
 
221
- for line in process.stderr:
222
- print(line, end='')
397
+ if __name__ == "__main__":
398
+ import argparse
223
399
 
224
- process.wait()
225
-
226
-
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
+ )
webscout/Extra/weather.py CHANGED
@@ -1,67 +1,172 @@
1
- import requests
2
- from rich.console import Console
3
- from rich.table import Table
4
- from yaspin import yaspin
5
- from pyfiglet import figlet_format
6
-
7
- console = Console()
8
-
9
- def get(location):
10
- """Fetches weather data for the given location.
11
-
12
- Args:
13
- location (str): The location for which to fetch weather data.
14
-
15
- Returns:
16
- dict: A dictionary containing weather data if the request is successful,
17
- otherwise a string indicating the error.
18
- """
19
- url = f"https://wttr.in/{location}?format=j1"
20
-
21
- with yaspin(text="Fetching weather data...") as spinner:
22
- response = requests.get(url)
23
- spinner.ok("✅ ")
24
-
25
- if response.status_code == 200:
26
- return response.json()
27
- else:
28
- return f"Error: Unable to fetch weather data. Status code: {response.status_code}"
29
-
30
- def print_weather(weather_data):
31
- """Prints the weather data in a user-friendly format.
32
-
33
- Args:
34
- weather_data (dict or str): The weather data returned from get_weather()
35
- or an error message.
36
- """
37
- if isinstance(weather_data, str):
38
- console.print(f"[bold red]Error:[/] {weather_data}")
39
- return
40
-
41
- current = weather_data['current_condition'][0]
42
- location_name = weather_data['nearest_area'][0]['areaName'][0]['value']
43
-
44
- console.print(f"[bold blue]\n{figlet_format('Weather Report')}[/]\n", justify="center")
45
- console.print(f"[bold green]Weather in {location_name}:[/]\n")
46
-
47
- table = Table(show_header=False, show_lines=True)
48
- table.add_row("Temperature:", f"{current['temp_C']C / {current['temp_F']}°F")
49
- table.add_row("Condition:", current['weatherDesc'][0]['value'])
50
- table.add_row("Humidity:", f"{current['humidity']}%")
51
- table.add_row("Wind:", f"{current['windspeedKmph']} km/h, {current['winddir16Point']}")
52
- console.print(table)
53
-
54
- console.print(f"\n[bold green]Forecast:[/]")
55
- table = Table(show_header=True, header_style="bold cyan")
56
- table.add_column("Date", style="dim", width=12)
57
- table.add_column("Temperature Range")
58
- table.add_column("Description")
59
-
60
- for day in weather_data['weather']:
61
- date = day['date']
62
- max_temp = day['maxtempC']
63
- min_temp = day['mintempC']
64
- desc = day['hourly'][4]['weatherDesc'][0]['value']
65
- table.add_row(date, f"{min_temp}°C to {max_temp}°C", desc)
66
- console.print(table)
67
-
1
+ import requests
2
+ import json
3
+ from datetime import datetime
4
+ from rich.console import Console
5
+ from rich.table import Table
6
+ from rich.panel import Panel
7
+ from rich.layout import Layout
8
+ from rich.align import Align
9
+ from rich import box
10
+ from rich.live import Live
11
+ from rich.progress import Progress, SpinnerColumn, TextColumn
12
+ from rich.style import Style
13
+ from rich.text import Text
14
+ from rich.columns import Columns
15
+
16
+ # Initialize Rich console with force terminal
17
+ console = Console(force_terminal=True)
18
+
19
+ def get_weather_emoji(condition: str) -> str:
20
+ """Get appropriate emoji for weather condition"""
21
+ conditions = {
22
+ 'sunny': '*', 'clear': '*',
23
+ 'partly cloudy': '~', 'cloudy': '=',
24
+ 'rain': 'v', 'light rain': '.',
25
+ 'heavy rain': 'V', 'thunderstorm': 'V',
26
+ 'snow': '*', 'light snow': '*',
27
+ 'mist': '-', 'fog': '-',
28
+ 'overcast': '='
29
+ }
30
+ condition = condition.lower()
31
+ for key, symbol in conditions.items():
32
+ if key in condition:
33
+ return symbol
34
+ return '~'
35
+
36
+ def get_wind_arrow(degrees: int) -> str:
37
+ """Convert wind degrees to arrow"""
38
+ arrows = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW']
39
+ index = round(degrees / 45) % 8
40
+ return arrows[index]
41
+
42
+ def format_temp(temp: str, scale='C') -> Text:
43
+ """Format temperature with color based on value"""
44
+ try:
45
+ value = float(temp)
46
+ if scale == 'C':
47
+ if value <= 0:
48
+ return Text(f"{temp}°{scale}", style="bold blue")
49
+ elif value >= 30:
50
+ return Text(f"{temp}°{scale}", style="bold red")
51
+ else:
52
+ return Text(f"{temp}°{scale}", style="bold green")
53
+ except ValueError:
54
+ pass
55
+ return Text(f"{temp}°{scale}")
56
+
57
+ def create_current_weather_panel(data):
58
+ """Create panel for current weather"""
59
+ current = data['current_condition'][0]
60
+ location = data['nearest_area'][0]
61
+ location_name = f"{location['areaName'][0]['value']}, {location['country'][0]['value']}"
62
+
63
+ weather_desc = current['weatherDesc'][0]['value']
64
+ symbol = get_weather_emoji(weather_desc)
65
+
66
+ # Create weather info table
67
+ table = Table(show_header=False, box=box.ROUNDED, expand=True)
68
+ table.add_column("Label", style="cyan")
69
+ table.add_column("Value", justify="right")
70
+
71
+ table.add_row("Location", f"@ {location_name}")
72
+ table.add_row("Condition", f"{symbol} {weather_desc}")
73
+ table.add_row("Temperature",
74
+ f"{format_temp(current['temp_C'])} / {format_temp(current['temp_F'], 'F')}")
75
+ table.add_row("Feels Like",
76
+ f"{format_temp(current['FeelsLikeC'])} / {format_temp(current['FeelsLikeF'], 'F')}")
77
+ table.add_row("Humidity", f"~ {current['humidity']}%")
78
+
79
+ wind_dir = get_wind_arrow(int(current['winddirDegree']))
80
+ table.add_row("Wind",
81
+ f"> {current['windspeedKmph']} km/h {wind_dir} ({current['winddir16Point']})")
82
+ table.add_row("Visibility", f"O {current['visibility']} km")
83
+ table.add_row("Pressure", f"# {current['pressure']} mb")
84
+
85
+ return Panel(table, title="[bold]Current Weather[/]", border_style="blue")
86
+
87
+ def create_forecast_panel(data):
88
+ """Create panel for weather forecast"""
89
+ table = Table(show_header=True, box=box.ROUNDED, expand=True)
90
+ table.add_column("Date", style="cyan")
91
+ table.add_column("Condition")
92
+ table.add_column("Temp (°C)")
93
+ table.add_column("Rain")
94
+ table.add_column("Wind")
95
+
96
+ for day in data['weather'][:3]:
97
+ date = datetime.strptime(day['date'], '%Y-%m-%d').strftime('%a, %b %d')
98
+ # Get mid-day conditions (noon)
99
+ noon = day['hourly'][4]
100
+ condition = noon['weatherDesc'][0]['value']
101
+ symbol = get_weather_emoji(condition)
102
+ temp_range = f"{day['mintempC']}° - {day['maxtempC']}°"
103
+ rain_chance = f"v {noon['chanceofrain']}%"
104
+ wind = f"> {noon['windspeedKmph']} km/h"
105
+
106
+ table.add_row(
107
+ date,
108
+ f"{symbol} {condition}",
109
+ temp_range,
110
+ rain_chance,
111
+ wind
112
+ )
113
+
114
+ return Panel(table, title="[bold]3-Day Forecast[/]", border_style="blue")
115
+
116
+ def get_weather(location: str):
117
+ """Get weather data with progress indicator"""
118
+ with Progress(
119
+ SpinnerColumn(),
120
+ TextColumn("[progress.description]{task.description}"),
121
+ console=console,
122
+ ) as progress:
123
+ progress.add_task(description="Fetching weather data...", total=None)
124
+ try:
125
+ response = requests.get(f"https://wttr.in/{location}?format=j1", timeout=10)
126
+ response.raise_for_status()
127
+ return response.json()
128
+ except Exception as e:
129
+ console.print(f"[red]Error fetching weather data: {str(e)}[/]")
130
+ return None
131
+
132
+ def display_weather(data):
133
+ """Display weather information in a beautiful layout"""
134
+ if not data:
135
+ return
136
+
137
+ # Create layout
138
+ layout = Layout()
139
+ layout.split_column(
140
+ Layout(name="current", size=15),
141
+ Layout(name="forecast", size=10)
142
+ )
143
+
144
+ # Update layout sections
145
+ layout["current"].update(create_current_weather_panel(data))
146
+ layout["forecast"].update(create_forecast_panel(data))
147
+
148
+ # Print layout with a title
149
+ console.print("\n")
150
+ console.print(Align.center("[bold blue]Weather Report[/]"))
151
+ console.print("\n")
152
+ console.print(layout)
153
+ console.print("\n")
154
+
155
+ def main():
156
+ """Main function to run the weather app"""
157
+ try:
158
+ console.clear()
159
+ console.print("\n[bold cyan]* Weather Information[/]\n")
160
+ location = console.input("[cyan]Enter location: [/]")
161
+
162
+ weather_data = get_weather(location)
163
+ if weather_data:
164
+ display_weather(weather_data)
165
+
166
+ except KeyboardInterrupt:
167
+ console.print("\n[yellow]Operation cancelled by user[/]")
168
+ except Exception as e:
169
+ console.print(f"\n[bold red]Unexpected error:[/] {str(e)}")
170
+
171
+ if __name__ == "__main__":
172
+ main()