webscout 7.5__py3-none-any.whl → 7.7__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/AIauto.py +5 -53
- webscout/AIutel.py +8 -318
- webscout/DWEBS.py +460 -489
- webscout/Extra/YTToolkit/YTdownloader.py +14 -53
- webscout/Extra/YTToolkit/transcriber.py +12 -13
- webscout/Extra/YTToolkit/ytapi/video.py +0 -1
- webscout/Extra/__init__.py +0 -1
- webscout/Extra/autocoder/__init__.py +9 -9
- webscout/Extra/autocoder/autocoder_utiles.py +193 -199
- webscout/Extra/autocoder/rawdog.py +789 -677
- webscout/Extra/gguf.py +682 -428
- webscout/Extra/weather.py +178 -156
- webscout/Extra/weather_ascii.py +70 -17
- webscout/Litlogger/core/logger.py +1 -2
- webscout/Litlogger/handlers/file.py +1 -1
- webscout/Litlogger/styles/formats.py +0 -2
- webscout/Litlogger/utils/detectors.py +0 -1
- webscout/Provider/AISEARCH/DeepFind.py +0 -1
- webscout/Provider/AISEARCH/ISou.py +1 -22
- webscout/Provider/AISEARCH/felo_search.py +0 -1
- webscout/Provider/AllenAI.py +28 -30
- webscout/Provider/C4ai.py +29 -11
- webscout/Provider/ChatGPTClone.py +226 -0
- webscout/Provider/ChatGPTGratis.py +24 -56
- webscout/Provider/DeepSeek.py +25 -17
- webscout/Provider/Deepinfra.py +115 -48
- webscout/Provider/Gemini.py +1 -1
- webscout/Provider/Glider.py +33 -12
- webscout/Provider/HF_space/qwen_qwen2.py +2 -2
- webscout/Provider/HeckAI.py +23 -7
- webscout/Provider/Hunyuan.py +272 -0
- webscout/Provider/Jadve.py +20 -5
- webscout/Provider/LambdaChat.py +391 -0
- webscout/Provider/Netwrck.py +42 -19
- webscout/Provider/OLLAMA.py +256 -32
- webscout/Provider/PI.py +4 -2
- webscout/Provider/Perplexitylabs.py +26 -6
- webscout/Provider/PizzaGPT.py +10 -51
- webscout/Provider/TTI/AiForce/async_aiforce.py +4 -37
- webscout/Provider/TTI/AiForce/sync_aiforce.py +41 -38
- webscout/Provider/TTI/FreeAIPlayground/__init__.py +9 -9
- webscout/Provider/TTI/FreeAIPlayground/async_freeaiplayground.py +179 -206
- webscout/Provider/TTI/FreeAIPlayground/sync_freeaiplayground.py +180 -192
- webscout/Provider/TTI/MagicStudio/__init__.py +2 -0
- webscout/Provider/TTI/MagicStudio/async_magicstudio.py +111 -0
- webscout/Provider/TTI/MagicStudio/sync_magicstudio.py +109 -0
- webscout/Provider/TTI/PollinationsAI/async_pollinations.py +5 -24
- webscout/Provider/TTI/PollinationsAI/sync_pollinations.py +2 -22
- webscout/Provider/TTI/__init__.py +2 -3
- webscout/Provider/TTI/aiarta/async_aiarta.py +14 -14
- webscout/Provider/TTI/aiarta/sync_aiarta.py +52 -21
- webscout/Provider/TTI/artbit/async_artbit.py +3 -32
- webscout/Provider/TTI/artbit/sync_artbit.py +3 -31
- webscout/Provider/TTI/fastflux/__init__.py +22 -0
- webscout/Provider/TTI/fastflux/async_fastflux.py +261 -0
- webscout/Provider/TTI/fastflux/sync_fastflux.py +252 -0
- webscout/Provider/TTI/piclumen/__init__.py +22 -22
- webscout/Provider/TTI/piclumen/sync_piclumen.py +232 -232
- webscout/Provider/TTS/__init__.py +2 -2
- webscout/Provider/TTS/deepgram.py +12 -39
- webscout/Provider/TTS/elevenlabs.py +14 -40
- webscout/Provider/TTS/gesserit.py +11 -35
- webscout/Provider/TTS/murfai.py +13 -39
- webscout/Provider/TTS/parler.py +17 -40
- webscout/Provider/TTS/speechma.py +180 -0
- webscout/Provider/TTS/streamElements.py +17 -44
- webscout/Provider/TextPollinationsAI.py +39 -59
- webscout/Provider/Venice.py +25 -8
- webscout/Provider/WebSim.py +227 -0
- webscout/Provider/WiseCat.py +27 -5
- webscout/Provider/Youchat.py +64 -37
- webscout/Provider/__init__.py +12 -7
- webscout/Provider/akashgpt.py +20 -5
- webscout/Provider/flowith.py +33 -7
- webscout/Provider/freeaichat.py +32 -45
- webscout/Provider/koala.py +20 -5
- webscout/Provider/labyrinth.py +239 -0
- webscout/Provider/learnfastai.py +28 -15
- webscout/Provider/llamatutor.py +1 -1
- webscout/Provider/llmchat.py +30 -8
- webscout/Provider/multichat.py +65 -9
- webscout/Provider/sonus.py +208 -0
- webscout/Provider/talkai.py +1 -0
- webscout/Provider/turboseek.py +3 -0
- webscout/Provider/tutorai.py +2 -0
- webscout/Provider/typegpt.py +155 -65
- webscout/Provider/uncovr.py +297 -0
- webscout/Provider/x0gpt.py +3 -1
- webscout/Provider/yep.py +102 -20
- webscout/__init__.py +3 -0
- webscout/cli.py +53 -40
- webscout/conversation.py +1 -10
- webscout/litagent/__init__.py +2 -2
- webscout/litagent/agent.py +356 -20
- webscout/litagent/constants.py +34 -5
- webscout/litprinter/__init__.py +0 -3
- webscout/models.py +181 -0
- webscout/optimizers.py +1 -1
- webscout/prompt_manager.py +2 -8
- webscout/scout/core/scout.py +1 -4
- webscout/scout/core/search_result.py +1 -1
- webscout/scout/core/text_utils.py +1 -1
- webscout/scout/core.py +2 -5
- webscout/scout/element.py +1 -1
- webscout/scout/parsers/html_parser.py +1 -1
- webscout/scout/utils.py +0 -1
- webscout/swiftcli/__init__.py +1 -3
- webscout/tempid.py +1 -1
- webscout/update_checker.py +1 -3
- webscout/version.py +1 -1
- webscout/webscout_search_async.py +1 -2
- webscout/yep_search.py +297 -297
- {webscout-7.5.dist-info → webscout-7.7.dist-info}/LICENSE.md +4 -4
- {webscout-7.5.dist-info → webscout-7.7.dist-info}/METADATA +127 -405
- {webscout-7.5.dist-info → webscout-7.7.dist-info}/RECORD +118 -117
- webscout/Extra/autollama.py +0 -231
- webscout/Provider/Amigo.py +0 -274
- webscout/Provider/Bing.py +0 -243
- webscout/Provider/DiscordRocks.py +0 -253
- webscout/Provider/TTI/blackbox/__init__.py +0 -4
- webscout/Provider/TTI/blackbox/async_blackbox.py +0 -212
- webscout/Provider/TTI/blackbox/sync_blackbox.py +0 -199
- webscout/Provider/TTI/deepinfra/__init__.py +0 -4
- webscout/Provider/TTI/deepinfra/async_deepinfra.py +0 -227
- webscout/Provider/TTI/deepinfra/sync_deepinfra.py +0 -199
- webscout/Provider/TTI/imgninza/__init__.py +0 -4
- webscout/Provider/TTI/imgninza/async_ninza.py +0 -214
- webscout/Provider/TTI/imgninza/sync_ninza.py +0 -209
- webscout/Provider/TTS/voicepod.py +0 -117
- {webscout-7.5.dist-info → webscout-7.7.dist-info}/WHEEL +0 -0
- {webscout-7.5.dist-info → webscout-7.7.dist-info}/entry_points.txt +0 -0
- {webscout-7.5.dist-info → webscout-7.7.dist-info}/top_level.txt +0 -0
webscout/Extra/weather.py
CHANGED
|
@@ -1,172 +1,194 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Weather information module with a clean, strongly-typed API structure.
|
|
3
|
+
|
|
4
|
+
This module provides a simple client for fetching weather data
|
|
5
|
+
from the wttr.in service with proper typing and a consistent interface.
|
|
6
|
+
"""
|
|
7
|
+
|
|
1
8
|
import requests
|
|
2
|
-
import json
|
|
3
9
|
from datetime import datetime
|
|
4
|
-
from
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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_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_emoji(weather_desc)
|
|
10
|
+
from typing import List, Dict, Any, Optional
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CurrentCondition:
|
|
14
|
+
"""Current weather conditions with strongly typed properties."""
|
|
65
15
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
16
|
+
def __init__(self, data: Dict[str, Any]) -> None:
|
|
17
|
+
"""Initialize with current condition data.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
data: Current condition data dictionary from wttr.in
|
|
21
|
+
"""
|
|
22
|
+
self.temp_c: Optional[str] = data.get('temp_C')
|
|
23
|
+
self.temp_f: Optional[str] = data.get('temp_F')
|
|
24
|
+
self.feels_like_c: Optional[str] = data.get('FeelsLikeC')
|
|
25
|
+
self.feels_like_f: Optional[str] = data.get('FeelsLikeF')
|
|
26
|
+
self.weather_desc: str = data.get('weatherDesc', [{}])[0].get('value', '')
|
|
27
|
+
self.weather_code: Optional[str] = data.get('weatherCode')
|
|
28
|
+
self.humidity: Optional[str] = data.get('humidity')
|
|
29
|
+
self.visibility: Optional[str] = data.get('visibility')
|
|
30
|
+
self.pressure: Optional[str] = data.get('pressure')
|
|
31
|
+
self.wind_speed_kmph: Optional[str] = data.get('windspeedKmph')
|
|
32
|
+
self.wind_direction: Optional[str] = data.get('winddir16Point')
|
|
33
|
+
self.wind_degree: Optional[str] = data.get('winddirDegree')
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class Location:
|
|
37
|
+
"""Location information with strongly typed properties."""
|
|
70
38
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
39
|
+
def __init__(self, data: Dict[str, Any]) -> None:
|
|
40
|
+
"""Initialize with location data.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
data: Location data dictionary from wttr.in
|
|
44
|
+
"""
|
|
45
|
+
self.name: str = data.get('areaName', [{}])[0].get('value', '')
|
|
46
|
+
self.country: str = data.get('country', [{}])[0].get('value', '')
|
|
47
|
+
self.region: str = data.get('region', [{}])[0].get('value', '')
|
|
48
|
+
self.latitude: Optional[str] = data.get('latitude')
|
|
49
|
+
self.longitude: Optional[str] = data.get('longitude')
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class HourlyForecast:
|
|
53
|
+
"""Hourly forecast information with strongly typed properties."""
|
|
78
54
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
55
|
+
def __init__(self, data: Dict[str, Any]) -> None:
|
|
56
|
+
"""Initialize with hourly forecast data.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
data: Hourly forecast data dictionary from wttr.in
|
|
60
|
+
"""
|
|
61
|
+
self.time: Optional[str] = data.get('time')
|
|
62
|
+
self.temp_c: Optional[str] = data.get('tempC')
|
|
63
|
+
self.temp_f: Optional[str] = data.get('tempF')
|
|
64
|
+
self.weather_desc: str = data.get('weatherDesc', [{}])[0].get('value', '')
|
|
65
|
+
self.weather_code: Optional[str] = data.get('weatherCode')
|
|
66
|
+
self.wind_speed_kmph: Optional[str] = data.get('windspeedKmph')
|
|
67
|
+
self.wind_direction: Optional[str] = data.get('winddir16Point')
|
|
68
|
+
self.feels_like_c: Optional[str] = data.get('FeelsLikeC')
|
|
69
|
+
self.feels_like_f: Optional[str] = data.get('FeelsLikeF')
|
|
70
|
+
self.chance_of_rain: Optional[str] = data.get('chanceofrain')
|
|
71
|
+
self.chance_of_snow: Optional[str] = data.get('chanceofsnow')
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class DayForecast:
|
|
75
|
+
"""Daily forecast information with strongly typed properties."""
|
|
84
76
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
77
|
+
def __init__(self, data: Dict[str, Any]) -> None:
|
|
78
|
+
"""Initialize with daily forecast data.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
data: Daily forecast data dictionary from wttr.in
|
|
82
|
+
"""
|
|
83
|
+
self.date: Optional[str] = data.get('date')
|
|
84
|
+
self.date_formatted: Optional[str] = None
|
|
85
|
+
if self.date:
|
|
86
|
+
try:
|
|
87
|
+
self.date_formatted = datetime.strptime(self.date, '%Y-%m-%d').strftime('%a, %b %d')
|
|
88
|
+
except ValueError:
|
|
89
|
+
pass
|
|
90
|
+
|
|
91
|
+
self.max_temp_c: Optional[str] = data.get('maxtempC')
|
|
92
|
+
self.max_temp_f: Optional[str] = data.get('maxtempF')
|
|
93
|
+
self.min_temp_c: Optional[str] = data.get('mintempC')
|
|
94
|
+
self.min_temp_f: Optional[str] = data.get('mintempF')
|
|
95
|
+
self.avg_temp_c: Optional[str] = data.get('avgtempC')
|
|
96
|
+
self.avg_temp_f: Optional[str] = data.get('avgtempF')
|
|
97
|
+
self.sun_hour: Optional[str] = data.get('sunHour')
|
|
98
|
+
|
|
99
|
+
# Parse astronomy data (simplified)
|
|
100
|
+
if data.get('astronomy') and len(data.get('astronomy', [])) > 0:
|
|
101
|
+
astro = data.get('astronomy', [{}])[0]
|
|
102
|
+
self.sunrise: Optional[str] = astro.get('sunrise')
|
|
103
|
+
self.sunset: Optional[str] = astro.get('sunset')
|
|
104
|
+
self.moon_phase: Optional[str] = astro.get('moon_phase')
|
|
105
|
+
else:
|
|
106
|
+
self.sunrise = self.sunset = self.moon_phase = None
|
|
107
|
+
|
|
108
|
+
# Parse hourly forecasts
|
|
109
|
+
self.hourly: List[HourlyForecast] = []
|
|
110
|
+
for hour_data in data.get('hourly', []):
|
|
111
|
+
self.hourly.append(HourlyForecast(hour_data))
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class Weather:
|
|
115
|
+
"""Weather response object with strongly typed properties."""
|
|
95
116
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
# Get mid-day conditions (noon)
|
|
99
|
-
noon = day['hourly'][4]
|
|
100
|
-
condition = noon['weatherDesc'][0]['value']
|
|
101
|
-
symbol = get_emoji(condition)
|
|
102
|
-
temp_range = f"{day['mintempC']}° - {day['maxtempC']}°"
|
|
103
|
-
rain_chance = f"v {noon['chanceofrain']}%"
|
|
104
|
-
wind = f"> {noon['windspeedKmph']} km/h"
|
|
117
|
+
def __init__(self, data: Optional[Dict[str, Any]] = None) -> None:
|
|
118
|
+
"""Initialize with weather data.
|
|
105
119
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
120
|
+
Args:
|
|
121
|
+
data: Weather data dictionary from wttr.in
|
|
122
|
+
"""
|
|
123
|
+
if not data:
|
|
124
|
+
self.current_condition = None
|
|
125
|
+
self.location = None
|
|
126
|
+
self.forecast_days = []
|
|
127
|
+
return
|
|
128
|
+
|
|
129
|
+
# Parse current condition
|
|
130
|
+
self.current_condition: Optional[CurrentCondition] = None
|
|
131
|
+
if data.get('current_condition') and len(data.get('current_condition', [])) > 0:
|
|
132
|
+
self.current_condition = CurrentCondition(data.get('current_condition', [{}])[0])
|
|
133
|
+
|
|
134
|
+
# Parse location
|
|
135
|
+
self.location: Optional[Location] = None
|
|
136
|
+
if data.get('nearest_area') and len(data.get('nearest_area', [])) > 0:
|
|
137
|
+
self.location = Location(data.get('nearest_area', [{}])[0])
|
|
138
|
+
|
|
139
|
+
# Parse forecast days
|
|
140
|
+
self.forecast_days: List[DayForecast] = []
|
|
141
|
+
for day_data in data.get('weather', []):
|
|
142
|
+
self.forecast_days.append(DayForecast(day_data))
|
|
143
|
+
|
|
144
|
+
@property
|
|
145
|
+
def today(self) -> Optional[DayForecast]:
|
|
146
|
+
"""Get today's forecast."""
|
|
147
|
+
return self.forecast_days[0] if self.forecast_days else None
|
|
148
|
+
|
|
149
|
+
@property
|
|
150
|
+
def tomorrow(self) -> Optional[DayForecast]:
|
|
151
|
+
"""Get tomorrow's forecast."""
|
|
152
|
+
return self.forecast_days[1] if len(self.forecast_days) > 1 else None
|
|
153
|
+
|
|
154
|
+
@property
|
|
155
|
+
def summary(self) -> str:
|
|
156
|
+
"""Get a simple text summary of current weather."""
|
|
157
|
+
if not self.current_condition or not self.location:
|
|
158
|
+
return "Weather data not available"
|
|
159
|
+
|
|
160
|
+
return f"{self.location.name}, {self.location.country}: {self.current_condition.weather_desc}, {self.current_condition.temp_c}°C ({self.current_condition.temp_f}°F)"
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class WeatherClient:
|
|
164
|
+
"""Client for fetching weather information."""
|
|
113
165
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
progress.add_task(description="Fetching weather data...", total=None)
|
|
166
|
+
def get_weather(self, location: str) -> Weather:
|
|
167
|
+
"""Get weather for the specified location.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
location: Location to get weather for (city name, zip code, etc.)
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
Weather object containing all weather data
|
|
174
|
+
"""
|
|
124
175
|
try:
|
|
125
176
|
response = requests.get(f"https://wttr.in/{location}?format=j1", timeout=10)
|
|
126
177
|
response.raise_for_status()
|
|
127
|
-
return response.json()
|
|
178
|
+
return Weather(response.json())
|
|
128
179
|
except Exception as e:
|
|
129
|
-
|
|
130
|
-
return
|
|
180
|
+
print(f"Error fetching weather data: {str(e)}")
|
|
181
|
+
return Weather()
|
|
131
182
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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))
|
|
183
|
+
|
|
184
|
+
def get(location: str) -> Weather:
|
|
185
|
+
"""Convenience function to get weather for a location.
|
|
147
186
|
|
|
148
|
-
|
|
149
|
-
|
|
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: [/]")
|
|
187
|
+
Args:
|
|
188
|
+
location: Location to get weather for
|
|
161
189
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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()
|
|
190
|
+
Returns:
|
|
191
|
+
Weather object containing all weather data
|
|
192
|
+
"""
|
|
193
|
+
client = WeatherClient()
|
|
194
|
+
return client.get_weather(location)
|
webscout/Extra/weather_ascii.py
CHANGED
|
@@ -1,23 +1,76 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Weather ASCII art visualization module with a simple, strongly-typed API.
|
|
3
|
+
|
|
4
|
+
This module provides a clean interface for fetching weather information
|
|
5
|
+
in ASCII art format using the wttr.in service.
|
|
6
|
+
"""
|
|
7
|
+
|
|
1
8
|
import requests
|
|
2
|
-
from
|
|
3
|
-
from webscout.zeroart import figlet_format
|
|
9
|
+
from typing import Dict, Optional, Any
|
|
4
10
|
|
|
5
|
-
console = Console()
|
|
6
|
-
def get(location):
|
|
7
|
-
"""Fetches ASCII art weather data for the given location.
|
|
8
|
-
Args:
|
|
9
|
-
location (str): The location for which to fetch weather data.
|
|
10
11
|
|
|
12
|
+
class WeatherAscii:
|
|
13
|
+
"""Container for ASCII weather data with a simple API."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, content: str) -> None:
|
|
16
|
+
"""Initialize with ASCII weather content.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
content: ASCII weather data or error message
|
|
20
|
+
"""
|
|
21
|
+
self._content = content
|
|
22
|
+
|
|
23
|
+
@property
|
|
24
|
+
def content(self) -> str:
|
|
25
|
+
"""Get the ASCII content, similar to choices.message.content in OpenAI API."""
|
|
26
|
+
return self._content
|
|
27
|
+
|
|
28
|
+
def __str__(self) -> str:
|
|
29
|
+
"""String representation of ASCII weather."""
|
|
30
|
+
return self.content
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class WeatherAsciiClient:
|
|
34
|
+
"""Client for fetching weather information in ASCII art."""
|
|
35
|
+
|
|
36
|
+
def get_weather(self, location: str, params: Optional[Dict[str, Any]] = None) -> WeatherAscii:
|
|
37
|
+
"""Get ASCII weather for a location.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
location: The location for which to fetch weather data
|
|
41
|
+
params: Additional parameters for the request
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
WeatherAscii object containing ASCII art weather data
|
|
45
|
+
"""
|
|
46
|
+
url = f"https://wttr.in/{location}"
|
|
47
|
+
headers = {'User-Agent': 'curl'}
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
response = requests.get(url, headers=headers, params=params, timeout=10)
|
|
51
|
+
response.raise_for_status()
|
|
52
|
+
|
|
53
|
+
if response.status_code == 200:
|
|
54
|
+
# Remove the footer line from wttr.in
|
|
55
|
+
ascii_weather = "\n".join(response.text.splitlines()[:-1])
|
|
56
|
+
return WeatherAscii(ascii_weather)
|
|
57
|
+
else:
|
|
58
|
+
error_msg = f"Error: Unable to fetch weather data. Status code: {response.status_code}"
|
|
59
|
+
return WeatherAscii(error_msg)
|
|
60
|
+
except requests.exceptions.RequestException as e:
|
|
61
|
+
return WeatherAscii(f"Error: {str(e)}")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def get(location: str, params: Optional[Dict[str, Any]] = None) -> WeatherAscii:
|
|
65
|
+
"""Convenience function to get ASCII weather for a location.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
location: Location to get weather for
|
|
69
|
+
params: Additional parameters for the request
|
|
70
|
+
|
|
11
71
|
Returns:
|
|
12
|
-
|
|
13
|
-
otherwise an error message.
|
|
72
|
+
WeatherAscii object containing ASCII art weather data
|
|
14
73
|
"""
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
response = requests.get(url, headers={'User-Agent': 'curl'})
|
|
18
|
-
|
|
19
|
-
if response.status_code == 200:
|
|
20
|
-
return "\n".join(response.text.splitlines()[:-1])
|
|
21
|
-
else:
|
|
22
|
-
return f"Error: Unable to fetch weather data. Status code: {response.status_code}"
|
|
74
|
+
client = WeatherAsciiClient()
|
|
75
|
+
return client.get_weather(location, params)
|
|
23
76
|
|
|
@@ -4,11 +4,10 @@ import sys
|
|
|
4
4
|
import threading
|
|
5
5
|
import traceback
|
|
6
6
|
from datetime import datetime
|
|
7
|
-
from typing import
|
|
7
|
+
from typing import List, Union
|
|
8
8
|
|
|
9
9
|
from ..core.level import LogLevel
|
|
10
10
|
from ..styles.formats import LogFormat
|
|
11
|
-
from ..styles.colors import LogColors
|
|
12
11
|
|
|
13
12
|
class Logger:
|
|
14
13
|
# Emoji mappings for different log levels
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import requests
|
|
2
2
|
import json
|
|
3
3
|
import re
|
|
4
|
-
from typing import Dict, Optional, Generator,
|
|
4
|
+
from typing import Dict, Optional, Generator, Any
|
|
5
5
|
from webscout import LitAgent
|
|
6
6
|
from webscout import exceptions
|
|
7
7
|
from webscout.AIbase import AISearch
|
|
@@ -65,7 +65,6 @@ class Isou(AISearch):
|
|
|
65
65
|
timeout: int = 120,
|
|
66
66
|
proxies: Optional[dict] = None,
|
|
67
67
|
model: str = "siliconflow:deepseek-ai/DeepSeek-R1-Distill-Qwen-32B",
|
|
68
|
-
logging: bool = False
|
|
69
68
|
):
|
|
70
69
|
"""Initialize the Isou API client.
|
|
71
70
|
|
|
@@ -73,7 +72,6 @@ class Isou(AISearch):
|
|
|
73
72
|
timeout (int, optional): Request timeout in seconds. Defaults to 120.
|
|
74
73
|
proxies (dict, optional): Proxy configuration for requests. Defaults to None.
|
|
75
74
|
model (str, optional): Model to use for search. Defaults to DeepSeek-R1.
|
|
76
|
-
logging (bool, optional): Enable logging. Defaults to False.
|
|
77
75
|
"""
|
|
78
76
|
self.available_models = [
|
|
79
77
|
"siliconflow:deepseek-ai/DeepSeek-R1-Distill-Qwen-32B",
|
|
@@ -115,24 +113,6 @@ class Isou(AISearch):
|
|
|
115
113
|
self.session.headers.update(self.headers)
|
|
116
114
|
self.proxies = proxies
|
|
117
115
|
|
|
118
|
-
# Initialize logger if enabled
|
|
119
|
-
if logging:
|
|
120
|
-
from webscout.Litlogger import Logger, LogFormat, ConsoleHandler
|
|
121
|
-
from webscout.Litlogger.core.level import LogLevel
|
|
122
|
-
|
|
123
|
-
console_handler = ConsoleHandler(
|
|
124
|
-
level=LogLevel.DEBUG,
|
|
125
|
-
)
|
|
126
|
-
|
|
127
|
-
self.logger = Logger(
|
|
128
|
-
name="Isou",
|
|
129
|
-
level=LogLevel.DEBUG,
|
|
130
|
-
handlers=[console_handler]
|
|
131
|
-
)
|
|
132
|
-
self.logger.info("Isou initialized successfully ✨")
|
|
133
|
-
else:
|
|
134
|
-
self.logger = None
|
|
135
|
-
|
|
136
116
|
def search(
|
|
137
117
|
self,
|
|
138
118
|
prompt: str,
|
|
@@ -268,7 +248,6 @@ if __name__ == "__main__":
|
|
|
268
248
|
# Initialize with specific model and logging
|
|
269
249
|
ai = Isou(
|
|
270
250
|
model="siliconflow:deepseek-ai/DeepSeek-R1-Distill-Qwen-32B",
|
|
271
|
-
logging=False
|
|
272
251
|
)
|
|
273
252
|
|
|
274
253
|
response = ai.search(input(">>> "), stream=True, raw=False)
|
webscout/Provider/AllenAI.py
CHANGED
|
@@ -18,10 +18,10 @@ class AllenAI(Provider):
|
|
|
18
18
|
|
|
19
19
|
AVAILABLE_MODELS = [
|
|
20
20
|
'tulu3-405b',
|
|
21
|
-
'OLMo-2-1124-13B-Instruct',
|
|
22
|
-
'tulu-3-1-8b',
|
|
23
|
-
'Llama-3-1-Tulu-3-70B',
|
|
24
|
-
'olmoe-0125'
|
|
21
|
+
# 'OLMo-2-1124-13B-Instruct',
|
|
22
|
+
# 'tulu-3-1-8b',
|
|
23
|
+
# 'Llama-3-1-Tulu-3-70B',
|
|
24
|
+
# 'olmoe-0125'
|
|
25
25
|
]
|
|
26
26
|
|
|
27
27
|
|
|
@@ -37,7 +37,7 @@ class AllenAI(Provider):
|
|
|
37
37
|
history_offset: int = 10250,
|
|
38
38
|
act: str = None,
|
|
39
39
|
model: str = "tulu3-405b",
|
|
40
|
-
|
|
40
|
+
|
|
41
41
|
):
|
|
42
42
|
"""Initializes the AllenAI API client."""
|
|
43
43
|
if model not in self.AVAILABLE_MODELS:
|
|
@@ -68,7 +68,6 @@ class AllenAI(Provider):
|
|
|
68
68
|
self.session.headers.update(self.headers)
|
|
69
69
|
self.session.proxies.update(proxies)
|
|
70
70
|
self.model = model
|
|
71
|
-
self.system_prompt = system_prompt
|
|
72
71
|
self.is_conversation = is_conversation
|
|
73
72
|
self.max_tokens_to_sample = max_tokens
|
|
74
73
|
self.timeout = timeout
|
|
@@ -95,19 +94,6 @@ class AllenAI(Provider):
|
|
|
95
94
|
)
|
|
96
95
|
self.conversation.history_offset = history_offset
|
|
97
96
|
|
|
98
|
-
def format_prompt(self, messages):
|
|
99
|
-
"""Format messages into a prompt string"""
|
|
100
|
-
formatted = []
|
|
101
|
-
for msg in messages:
|
|
102
|
-
role = msg.get("role", "")
|
|
103
|
-
content = msg.get("content", "")
|
|
104
|
-
if role == "system":
|
|
105
|
-
formatted.append(f"System: {content}")
|
|
106
|
-
elif role == "user":
|
|
107
|
-
formatted.append(f"User: {content}")
|
|
108
|
-
elif role == "assistant":
|
|
109
|
-
formatted.append(f"Assistant: {content}")
|
|
110
|
-
return "\n".join(formatted)
|
|
111
97
|
|
|
112
98
|
def ask(
|
|
113
99
|
self,
|
|
@@ -139,11 +125,8 @@ class AllenAI(Provider):
|
|
|
139
125
|
"x-anonymous-user-id": self.x_anonymous_user_id
|
|
140
126
|
})
|
|
141
127
|
|
|
142
|
-
|
|
143
|
-
messages =
|
|
144
|
-
{"role": "system", "content": self.system_prompt},
|
|
145
|
-
{"role": "user", "content": conversation_prompt}
|
|
146
|
-
]
|
|
128
|
+
|
|
129
|
+
messages = conversation_prompt
|
|
147
130
|
|
|
148
131
|
# Build multipart form data
|
|
149
132
|
form_data = [
|
|
@@ -154,7 +137,7 @@ class AllenAI(Provider):
|
|
|
154
137
|
f'Content-Disposition: form-data; name="host"\r\n\r\n{host}\r\n',
|
|
155
138
|
|
|
156
139
|
f'--{boundary}\r\n'
|
|
157
|
-
f'Content-Disposition: form-data; name="content"\r\n\r\n{
|
|
140
|
+
f'Content-Disposition: form-data; name="content"\r\n\r\n{messages}\r\n',
|
|
158
141
|
|
|
159
142
|
f'--{boundary}\r\n'
|
|
160
143
|
f'Content-Disposition: form-data; name="private"\r\n\r\n{str(private).lower()}\r\n'
|
|
@@ -275,8 +258,23 @@ class AllenAI(Provider):
|
|
|
275
258
|
return response["text"]
|
|
276
259
|
|
|
277
260
|
if __name__ == "__main__":
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
261
|
+
print("-" * 80)
|
|
262
|
+
print(f"{'Model':<50} {'Status':<10} {'Response'}")
|
|
263
|
+
print("-" * 80)
|
|
264
|
+
|
|
265
|
+
for model in AllenAI.AVAILABLE_MODELS:
|
|
266
|
+
try:
|
|
267
|
+
test_ai = AllenAI(model=model, timeout=60)
|
|
268
|
+
response = test_ai.chat("Say 'Hello' in one word")
|
|
269
|
+
response_text = response
|
|
270
|
+
|
|
271
|
+
if response_text and len(response_text.strip()) > 0:
|
|
272
|
+
status = "✓"
|
|
273
|
+
# Truncate response if too long
|
|
274
|
+
display_text = response_text.strip()[:50] + "..." if len(response_text.strip()) > 50 else response_text.strip()
|
|
275
|
+
else:
|
|
276
|
+
status = "✗"
|
|
277
|
+
display_text = "Empty or invalid response"
|
|
278
|
+
print(f"{model:<50} {status:<10} {display_text}")
|
|
279
|
+
except Exception as e:
|
|
280
|
+
print(f"{model:<50} {'✗':<10} {str(e)}")
|