webscout 8.3__py3-none-any.whl → 8.3.2__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 (120) hide show
  1. webscout/AIauto.py +4 -4
  2. webscout/AIbase.py +61 -1
  3. webscout/AIutel.py +46 -53
  4. webscout/Bing_search.py +418 -0
  5. webscout/Extra/YTToolkit/ytapi/patterns.py +45 -45
  6. webscout/Extra/YTToolkit/ytapi/stream.py +1 -1
  7. webscout/Extra/YTToolkit/ytapi/video.py +10 -10
  8. webscout/Extra/autocoder/autocoder_utiles.py +1 -1
  9. webscout/Extra/gguf.py +706 -177
  10. webscout/Litlogger/formats.py +9 -0
  11. webscout/Litlogger/handlers.py +18 -0
  12. webscout/Litlogger/logger.py +43 -1
  13. webscout/Provider/AISEARCH/genspark_search.py +7 -7
  14. webscout/Provider/AISEARCH/scira_search.py +3 -2
  15. webscout/Provider/GeminiProxy.py +140 -0
  16. webscout/Provider/LambdaChat.py +7 -1
  17. webscout/Provider/MCPCore.py +78 -75
  18. webscout/Provider/OPENAI/BLACKBOXAI.py +1046 -1017
  19. webscout/Provider/OPENAI/GeminiProxy.py +328 -0
  20. webscout/Provider/OPENAI/Qwen3.py +303 -303
  21. webscout/Provider/OPENAI/README.md +5 -0
  22. webscout/Provider/OPENAI/README_AUTOPROXY.md +238 -0
  23. webscout/Provider/OPENAI/TogetherAI.py +355 -0
  24. webscout/Provider/OPENAI/__init__.py +16 -1
  25. webscout/Provider/OPENAI/autoproxy.py +332 -0
  26. webscout/Provider/OPENAI/base.py +101 -14
  27. webscout/Provider/OPENAI/chatgpt.py +15 -2
  28. webscout/Provider/OPENAI/chatgptclone.py +14 -3
  29. webscout/Provider/OPENAI/deepinfra.py +339 -328
  30. webscout/Provider/OPENAI/e2b.py +295 -74
  31. webscout/Provider/OPENAI/mcpcore.py +109 -70
  32. webscout/Provider/OPENAI/opkfc.py +18 -6
  33. webscout/Provider/OPENAI/scirachat.py +59 -50
  34. webscout/Provider/OPENAI/toolbaz.py +2 -10
  35. webscout/Provider/OPENAI/writecream.py +166 -166
  36. webscout/Provider/OPENAI/x0gpt.py +367 -367
  37. webscout/Provider/OPENAI/xenai.py +514 -0
  38. webscout/Provider/OPENAI/yep.py +389 -383
  39. webscout/Provider/STT/__init__.py +3 -0
  40. webscout/Provider/STT/base.py +281 -0
  41. webscout/Provider/STT/elevenlabs.py +265 -0
  42. webscout/Provider/TTI/__init__.py +4 -1
  43. webscout/Provider/TTI/aiarta.py +399 -365
  44. webscout/Provider/TTI/base.py +74 -2
  45. webscout/Provider/TTI/bing.py +231 -0
  46. webscout/Provider/TTI/fastflux.py +63 -30
  47. webscout/Provider/TTI/gpt1image.py +149 -0
  48. webscout/Provider/TTI/imagen.py +196 -0
  49. webscout/Provider/TTI/magicstudio.py +60 -29
  50. webscout/Provider/TTI/piclumen.py +43 -32
  51. webscout/Provider/TTI/pixelmuse.py +232 -225
  52. webscout/Provider/TTI/pollinations.py +43 -32
  53. webscout/Provider/TTI/together.py +287 -0
  54. webscout/Provider/TTI/utils.py +2 -1
  55. webscout/Provider/TTS/README.md +1 -0
  56. webscout/Provider/TTS/__init__.py +2 -1
  57. webscout/Provider/TTS/freetts.py +140 -0
  58. webscout/Provider/TTS/speechma.py +45 -39
  59. webscout/Provider/TogetherAI.py +366 -0
  60. webscout/Provider/UNFINISHED/ChutesAI.py +314 -0
  61. webscout/Provider/UNFINISHED/fetch_together_models.py +95 -0
  62. webscout/Provider/XenAI.py +324 -0
  63. webscout/Provider/__init__.py +8 -0
  64. webscout/Provider/deepseek_assistant.py +378 -0
  65. webscout/Provider/scira_chat.py +3 -2
  66. webscout/Provider/toolbaz.py +0 -1
  67. webscout/auth/__init__.py +44 -0
  68. webscout/auth/api_key_manager.py +189 -0
  69. webscout/auth/auth_system.py +100 -0
  70. webscout/auth/config.py +76 -0
  71. webscout/auth/database.py +400 -0
  72. webscout/auth/exceptions.py +67 -0
  73. webscout/auth/middleware.py +248 -0
  74. webscout/auth/models.py +130 -0
  75. webscout/auth/providers.py +257 -0
  76. webscout/auth/rate_limiter.py +254 -0
  77. webscout/auth/request_models.py +127 -0
  78. webscout/auth/request_processing.py +226 -0
  79. webscout/auth/routes.py +526 -0
  80. webscout/auth/schemas.py +103 -0
  81. webscout/auth/server.py +312 -0
  82. webscout/auth/static/favicon.svg +11 -0
  83. webscout/auth/swagger_ui.py +203 -0
  84. webscout/auth/templates/components/authentication.html +237 -0
  85. webscout/auth/templates/components/base.html +103 -0
  86. webscout/auth/templates/components/endpoints.html +750 -0
  87. webscout/auth/templates/components/examples.html +491 -0
  88. webscout/auth/templates/components/footer.html +75 -0
  89. webscout/auth/templates/components/header.html +27 -0
  90. webscout/auth/templates/components/models.html +286 -0
  91. webscout/auth/templates/components/navigation.html +70 -0
  92. webscout/auth/templates/static/api.js +455 -0
  93. webscout/auth/templates/static/icons.js +168 -0
  94. webscout/auth/templates/static/main.js +784 -0
  95. webscout/auth/templates/static/particles.js +201 -0
  96. webscout/auth/templates/static/styles.css +3353 -0
  97. webscout/auth/templates/static/ui.js +374 -0
  98. webscout/auth/templates/swagger_ui.html +170 -0
  99. webscout/client.py +49 -3
  100. webscout/litagent/Readme.md +12 -3
  101. webscout/litagent/agent.py +99 -62
  102. webscout/scout/core/scout.py +104 -26
  103. webscout/scout/element.py +139 -18
  104. webscout/swiftcli/core/cli.py +14 -3
  105. webscout/swiftcli/decorators/output.py +59 -9
  106. webscout/update_checker.py +31 -49
  107. webscout/version.py +1 -1
  108. webscout/webscout_search.py +4 -12
  109. webscout/webscout_search_async.py +3 -10
  110. webscout/yep_search.py +2 -11
  111. {webscout-8.3.dist-info → webscout-8.3.2.dist-info}/METADATA +41 -11
  112. {webscout-8.3.dist-info → webscout-8.3.2.dist-info}/RECORD +116 -68
  113. {webscout-8.3.dist-info → webscout-8.3.2.dist-info}/entry_points.txt +1 -1
  114. webscout/Provider/HF_space/__init__.py +0 -0
  115. webscout/Provider/HF_space/qwen_qwen2.py +0 -206
  116. webscout/Provider/OPENAI/api.py +0 -1035
  117. webscout/Provider/TTI/artbit.py +0 -0
  118. {webscout-8.3.dist-info → webscout-8.3.2.dist-info}/WHEEL +0 -0
  119. {webscout-8.3.dist-info → webscout-8.3.2.dist-info}/licenses/LICENSE.md +0 -0
  120. {webscout-8.3.dist-info → webscout-8.3.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,332 @@
1
+ """
2
+ Auto-proxy module for OpenAI-compatible providers.
3
+ This module provides automatic proxy injection for HTTP sessions using a remote proxy list.
4
+ """
5
+
6
+ import random
7
+ import time
8
+ from abc import ABCMeta
9
+ from typing import Dict, List, Optional, Any
10
+ import requests
11
+
12
+ # Optional imports for different HTTP clients
13
+ try:
14
+ import httpx
15
+ except ImportError:
16
+ httpx = None
17
+
18
+ try:
19
+ from curl_cffi.requests import Session as CurlSession
20
+ from curl_cffi.requests import AsyncSession as CurlAsyncSession
21
+ except ImportError:
22
+ CurlSession = None
23
+ CurlAsyncSession = None
24
+
25
+ # Global proxy cache
26
+ _proxy_cache = {
27
+ 'proxies': [],
28
+ 'last_updated': 0,
29
+ 'cache_duration': 300 # 5 minutes
30
+ }
31
+
32
+ PROXY_SOURCE_URL = "http://207.180.209.185:5000/ips.txt"
33
+
34
+
35
+ def fetch_proxies() -> List[str]:
36
+ """
37
+ Fetch proxy list from the remote source.
38
+
39
+ Returns:
40
+ List[str]: List of proxy URLs in format 'http://user:pass@host:port'
41
+ """
42
+ try:
43
+ response = requests.get(PROXY_SOURCE_URL, timeout=10)
44
+ response.raise_for_status()
45
+
46
+ proxies = []
47
+ for line in response.text.strip().split('\n'):
48
+ line = line.strip()
49
+ if line and line.startswith('http://'):
50
+ proxies.append(line)
51
+
52
+ return proxies
53
+
54
+ except Exception:
55
+ return []
56
+
57
+
58
+ def get_cached_proxies() -> List[str]:
59
+ """
60
+ Get proxies from cache or fetch new ones if cache is expired.
61
+
62
+ Returns:
63
+ List[str]: List of proxy URLs
64
+ """
65
+ current_time = time.time()
66
+
67
+ # Check if cache is expired or empty
68
+ if (current_time - _proxy_cache['last_updated'] > _proxy_cache['cache_duration'] or
69
+ not _proxy_cache['proxies']):
70
+
71
+ new_proxies = fetch_proxies()
72
+ if new_proxies:
73
+ _proxy_cache['proxies'] = new_proxies
74
+ _proxy_cache['last_updated'] = current_time
75
+ else:
76
+ pass
77
+
78
+ return _proxy_cache['proxies']
79
+
80
+
81
+ def get_auto_proxy() -> Optional[str]:
82
+ """
83
+ Get a random proxy from the cached proxy list.
84
+
85
+ Returns:
86
+ Optional[str]: A proxy URL or None if no proxies available
87
+ """
88
+ proxies = get_cached_proxies()
89
+ if not proxies:
90
+ return None
91
+
92
+ proxy = random.choice(proxies)
93
+ return proxy
94
+
95
+
96
+ def get_proxy_dict(proxy_url: Optional[str] = None) -> Dict[str, str]:
97
+ """
98
+ Convert a proxy URL to a dictionary format suitable for requests/httpx.
99
+
100
+ Args:
101
+ proxy_url: Proxy URL, if None will get one automatically
102
+
103
+ Returns:
104
+ Dict[str, str]: Proxy dictionary with 'http' and 'https' keys
105
+ """
106
+ if proxy_url is None:
107
+ proxy_url = get_auto_proxy()
108
+
109
+ if proxy_url is None:
110
+ return {}
111
+
112
+ return {
113
+ 'http': proxy_url,
114
+ 'https': proxy_url
115
+ }
116
+
117
+
118
+ def test_proxy(proxy_url: str, timeout: int = 10) -> bool:
119
+ """
120
+ Test if a proxy is working by making a request to a test URL.
121
+
122
+ Args:
123
+ proxy_url: The proxy URL to test
124
+ timeout: Request timeout in seconds
125
+
126
+ Returns:
127
+ bool: True if proxy is working, False otherwise
128
+ """
129
+ try:
130
+ test_url = "https://httpbin.org/ip"
131
+ proxies = {'http': proxy_url, 'https': proxy_url}
132
+
133
+ response = requests.get(test_url, proxies=proxies, timeout=timeout)
134
+ return response.status_code == 200
135
+
136
+ except Exception:
137
+ return False
138
+
139
+
140
+ class ProxyAutoMeta(ABCMeta):
141
+ """
142
+ Metaclass to ensure all OpenAICompatibleProvider subclasses automatically get proxy support.
143
+ This will inject proxies into any requests.Session, httpx.Client, or curl_cffi session attributes found on the instance.
144
+
145
+ To disable automatic proxy injection, set disable_auto_proxy=True in the constructor or
146
+ set the class attribute DISABLE_AUTO_PROXY = True.
147
+ """
148
+
149
+ def __call__(cls, *args, **kwargs):
150
+ instance = super().__call__(*args, **kwargs)
151
+
152
+ # Check if auto proxy is disabled
153
+ disable_auto_proxy = kwargs.get('disable_auto_proxy', False) or getattr(cls, 'DISABLE_AUTO_PROXY', False)
154
+
155
+ # Get proxies from various sources
156
+ proxies = getattr(instance, 'proxies', None) or kwargs.get('proxies', None)
157
+
158
+ if proxies is None and not disable_auto_proxy:
159
+ try:
160
+ proxy_url = get_auto_proxy()
161
+ if proxy_url:
162
+ proxies = get_proxy_dict(proxy_url)
163
+ else:
164
+ proxies = {}
165
+ except Exception:
166
+ proxies = {}
167
+ elif proxies is None:
168
+ proxies = {}
169
+
170
+ instance.proxies = proxies
171
+
172
+ # Patch existing sessions if we have valid proxies
173
+ if proxies:
174
+ _patch_instance_sessions(instance, proxies)
175
+
176
+ # Provide helper methods for creating proxied sessions
177
+ _add_proxy_helpers(instance, proxies)
178
+
179
+ return instance
180
+
181
+
182
+ def _patch_instance_sessions(instance: Any, proxies: Dict[str, str]) -> None:
183
+ """
184
+ Patch existing session objects on the instance with proxy configuration.
185
+
186
+ Args:
187
+ instance: The class instance to patch
188
+ proxies: Proxy dictionary to apply
189
+ """
190
+ for attr_name in dir(instance):
191
+ if attr_name.startswith('_'):
192
+ continue
193
+
194
+ try:
195
+ attr_obj = getattr(instance, attr_name)
196
+
197
+ # Patch requests.Session objects
198
+ if isinstance(attr_obj, requests.Session):
199
+ attr_obj.proxies.update(proxies)
200
+
201
+ # Patch httpx.Client objects
202
+ elif httpx and isinstance(attr_obj, httpx.Client):
203
+ try:
204
+ # httpx uses different proxy format
205
+ attr_obj._proxies = proxies
206
+ except Exception:
207
+ pass
208
+
209
+ # Patch curl_cffi Session objects
210
+ elif CurlSession and isinstance(attr_obj, CurlSession):
211
+ try:
212
+ attr_obj.proxies.update(proxies)
213
+ except Exception:
214
+ pass
215
+
216
+ # Patch curl_cffi AsyncSession objects
217
+ elif CurlAsyncSession and isinstance(attr_obj, CurlAsyncSession):
218
+ try:
219
+ attr_obj.proxies.update(proxies)
220
+ except Exception:
221
+ pass
222
+
223
+ except Exception:
224
+ continue
225
+
226
+
227
+ def _add_proxy_helpers(instance: Any, proxies: Dict[str, str]) -> None:
228
+ """
229
+ Add helper methods to the instance for creating proxied sessions.
230
+
231
+ Args:
232
+ instance: The class instance to add methods to
233
+ proxies: Proxy dictionary to use in helper methods
234
+ """
235
+
236
+ def get_proxied_session():
237
+ """Get a requests.Session with proxies configured"""
238
+ session = requests.Session()
239
+ session.proxies.update(proxies)
240
+ return session
241
+
242
+ def get_proxied_httpx_client(**kwargs):
243
+ """Get an httpx.Client with proxies configured"""
244
+ if httpx:
245
+ return httpx.Client(proxies=proxies, **kwargs)
246
+ else:
247
+ raise ImportError("httpx is not installed")
248
+
249
+ def get_proxied_curl_session(impersonate="chrome120", **kwargs):
250
+ """Get a curl_cffi Session with proxies configured"""
251
+ if CurlSession:
252
+ return CurlSession(proxies=proxies, impersonate=impersonate, **kwargs)
253
+ else:
254
+ raise ImportError("curl_cffi is not installed")
255
+
256
+ def get_proxied_curl_async_session(impersonate="chrome120", **kwargs):
257
+ """Get a curl_cffi AsyncSession with proxies configured"""
258
+ if CurlAsyncSession:
259
+ return CurlAsyncSession(proxies=proxies, impersonate=impersonate, **kwargs)
260
+ else:
261
+ raise ImportError("curl_cffi is not installed")
262
+
263
+ # Add methods to instance
264
+ instance.get_proxied_session = get_proxied_session
265
+ instance.get_proxied_httpx_client = get_proxied_httpx_client
266
+ instance.get_proxied_curl_session = get_proxied_curl_session
267
+ instance.get_proxied_curl_async_session = get_proxied_curl_async_session
268
+
269
+
270
+ def get_working_proxy(max_attempts: int = 5, timeout: int = 10) -> Optional[str]:
271
+ """
272
+ Get a working proxy by testing multiple proxies from the list.
273
+
274
+ Args:
275
+ max_attempts: Maximum number of proxies to test
276
+ timeout: Timeout for each proxy test
277
+
278
+ Returns:
279
+ Optional[str]: A working proxy URL or None if none found
280
+ """
281
+ proxies = get_cached_proxies()
282
+ if not proxies:
283
+ return None
284
+
285
+ # Shuffle to avoid always testing the same proxies first
286
+ test_proxies = random.sample(proxies, min(max_attempts, len(proxies)))
287
+
288
+ for proxy in test_proxies:
289
+ if test_proxy(proxy, timeout):
290
+ return proxy
291
+
292
+ return None
293
+
294
+
295
+ def refresh_proxy_cache() -> int:
296
+ """
297
+ Force refresh the proxy cache.
298
+
299
+ Returns:
300
+ int: Number of proxies loaded
301
+ """
302
+ global _proxy_cache
303
+ _proxy_cache['last_updated'] = 0 # Force refresh
304
+ proxies = get_cached_proxies()
305
+ return len(proxies)
306
+
307
+
308
+ def get_proxy_stats() -> Dict[str, Any]:
309
+ """
310
+ Get statistics about the proxy cache.
311
+
312
+ Returns:
313
+ Dict[str, Any]: Statistics including count, last update time, etc.
314
+ """
315
+ return {
316
+ 'proxy_count': len(_proxy_cache['proxies']),
317
+ 'last_updated': _proxy_cache['last_updated'],
318
+ 'cache_duration': _proxy_cache['cache_duration'],
319
+ 'cache_age_seconds': time.time() - _proxy_cache['last_updated'],
320
+ 'source_url': PROXY_SOURCE_URL
321
+ }
322
+
323
+
324
+ def set_proxy_cache_duration(duration: int) -> None:
325
+ """
326
+ Set the proxy cache duration.
327
+
328
+ Args:
329
+ duration: Cache duration in seconds
330
+ """
331
+ global _proxy_cache
332
+ _proxy_cache['cache_duration'] = duration
@@ -1,14 +1,23 @@
1
1
  from abc import ABC, abstractmethod
2
2
  from typing import List, Dict, Optional, Union, Generator, Any, TypedDict, Callable
3
3
  import json
4
- import logging
5
4
  from dataclasses import dataclass
6
5
 
7
- logger = logging.getLogger(__name__)
6
+ # Import WebScout Litlogger instead of standard logging
7
+ from webscout.Litlogger import Logger, LogLevel
8
+
9
+ logger = Logger(name="OpenAIBase", level=LogLevel.INFO)
10
+
11
+ # Import the ProxyAutoMeta metaclass
12
+ try:
13
+ from .autoproxy import ProxyAutoMeta
14
+ except ImportError:
15
+ # Fallback if autoproxy is not available
16
+ ProxyAutoMeta = type
8
17
 
9
18
 
10
19
  # Import the utils for response structures
11
- from webscout.Provider.OPENAI.utils import ChatCompletion, ChatCompletionChunk, Choice, ChatCompletionMessage, ToolCall, ToolFunction
20
+ from webscout.Provider.OPENAI.utils import ChatCompletion, ChatCompletionChunk
12
21
 
13
22
  # Define tool-related structures
14
23
  class ToolDefinition(TypedDict):
@@ -174,10 +183,92 @@ class BaseChat(ABC):
174
183
  completions: BaseCompletions
175
184
 
176
185
 
177
- class OpenAICompatibleProvider(ABC):
186
+ # class ProxyAutoMeta(ABCMeta):
187
+ # """
188
+ # Metaclass to ensure all OpenAICompatibleProvider subclasses automatically get proxy support.
189
+ # This will inject proxies into any requests.Session, httpx.Client, or curl_cffi session attributes found on the instance.
190
+
191
+ # To disable automatic proxy injection, set disable_auto_proxy=True in the constructor or
192
+ # set the class attribute DISABLE_AUTO_PROXY = True.
193
+ # """
194
+ # def __call__(cls, *args, **kwargs):
195
+ # instance = super().__call__(*args, **kwargs)
196
+
197
+ # # Check if auto proxy is disabled
198
+ # disable_auto_proxy = kwargs.get('disable_auto_proxy', False) or getattr(cls, 'DISABLE_AUTO_PROXY', False)
199
+
200
+ # proxies = getattr(instance, 'proxies', None) or kwargs.get('proxies', None)
201
+ # if proxies is None and not disable_auto_proxy:
202
+ # try:
203
+ # proxies = {"http": get_auto_proxy(), "https": get_auto_proxy()}
204
+ # except Exception as e:
205
+ # logger.warning(f"Failed to get auto proxy, disabling proxy support: {e}")
206
+ # proxies = {}
207
+ # elif proxies is None:
208
+ # proxies = {}
209
+ # instance.proxies = proxies
210
+ # # Patch sessions if we have valid proxies
211
+ # if proxies:
212
+ # for attr in dir(instance):
213
+ # obj = getattr(instance, attr)
214
+ # if isinstance(obj, requests.Session):
215
+ # obj.proxies.update(proxies)
216
+ # if httpx and isinstance(obj, httpx.Client):
217
+ # try:
218
+ # obj._proxies = proxies
219
+ # except Exception:
220
+ # pass
221
+ # # Patch curl_cffi sessions if present
222
+ # if CurlSession and isinstance(obj, CurlSession):
223
+ # try:
224
+ # obj.proxies.update(proxies)
225
+ # except Exception:
226
+ # pass
227
+ # if CurlAsyncSession and isinstance(obj, CurlAsyncSession):
228
+ # try:
229
+ # obj.proxies.update(proxies)
230
+ # except Exception:
231
+ # pass
232
+ # # Provide helpers for proxied sessions
233
+ # def get_proxied_session():
234
+ # s = requests.Session()
235
+ # s.proxies.update(proxies)
236
+ # return s
237
+ # instance.get_proxied_session = get_proxied_session
238
+
239
+ # def get_proxied_curl_session(impersonate="chrome120", **kwargs):
240
+ # """Get a curl_cffi Session with proxies configured"""
241
+ # if CurlSession:
242
+ # return CurlSession(proxies=proxies, impersonate=impersonate, **kwargs)
243
+ # else:
244
+ # raise ImportError("curl_cffi is not installed")
245
+ # instance.get_proxied_curl_session = get_proxied_curl_session
246
+
247
+ # def get_proxied_curl_async_session(impersonate="chrome120", **kwargs):
248
+ # """Get a curl_cffi AsyncSession with proxies configured"""
249
+ # if CurlAsyncSession:
250
+ # return CurlAsyncSession(proxies=proxies, impersonate=impersonate, **kwargs)
251
+ # else:
252
+ # raise ImportError("curl_cffi is not installed")
253
+ # instance.get_proxied_curl_async_session = get_proxied_curl_async_session
254
+
255
+ # return instance
256
+ class OpenAICompatibleProvider(ABC, metaclass=ProxyAutoMeta):
178
257
  """
179
258
  Abstract Base Class for providers mimicking the OpenAI Python client structure.
180
259
  Requires a nested 'chat.completions' structure with tool support.
260
+ All subclasses automatically get proxy support via ProxyAutoMeta.
261
+
262
+ # Available proxy helpers:
263
+ # - self.get_proxied_session() - returns a requests.Session with proxies
264
+ # - self.get_proxied_curl_session() - returns a curl_cffi.Session with proxies
265
+ # - self.get_proxied_curl_async_session() - returns a curl_cffi.AsyncSession with proxies
266
+
267
+ # Proxy support is automatically injected into:
268
+ # - requests.Session objects
269
+ # - httpx.Client objects
270
+ # - curl_cffi.requests.Session objects
271
+ # - curl_cffi.requests.AsyncSession objects
181
272
  """
182
273
  chat: BaseChat
183
274
  available_tools: Dict[str, Tool] = {} # Dictionary of available tools
@@ -185,19 +276,15 @@ class OpenAICompatibleProvider(ABC):
185
276
  supports_tool_choice: bool = False # Whether the provider supports tool_choice
186
277
 
187
278
  @abstractmethod
188
- def __init__(self, api_key: Optional[str] = None, tools: Optional[List[Tool]] = None, **kwargs: Any):
189
- """
190
- Initialize the provider, potentially with an API key and tools.
191
-
192
- Args:
193
- api_key: Optional API key for the provider
194
- tools: Optional list of tools to make available to the provider
195
- **kwargs: Additional provider-specific parameters
196
- """
279
+ def __init__(self, api_key: Optional[str] = None, tools: Optional[List[Tool]] = None, proxies: Optional[dict] = None, disable_auto_proxy: bool = False, **kwargs: Any):
197
280
  self.available_tools = {}
198
281
  if tools:
199
282
  self.register_tools(tools)
200
- raise NotImplementedError
283
+ # self.proxies is set by ProxyAutoMeta
284
+ # Subclasses should use self.proxies for all network requests
285
+ # Optionally, use self.get_proxied_session() for a requests.Session with proxies
286
+ # The disable_auto_proxy parameter is handled by ProxyAutoMeta
287
+ # raise NotImplementedError # <-- Commented out for metaclass test
201
288
 
202
289
  @property
203
290
  @abstractmethod
@@ -37,9 +37,21 @@ class ChatGPTReversed:
37
37
  csrf_token = None
38
38
  initialized = False
39
39
 
40
+ _instance = None
41
+
42
+ def __new__(cls, model="auto"):
43
+ if cls._instance is None:
44
+ cls._instance = super(ChatGPTReversed, cls).__new__(cls)
45
+ cls._instance.initialized = False
46
+ return cls._instance
47
+
40
48
  def __init__(self, model="auto"):
41
- if ChatGPTReversed.initialized:
42
- raise Exception("ChatGPTReversed has already been initialized.")
49
+ if self.initialized:
50
+ # Already initialized, just update model if needed
51
+ if model not in self.AVAILABLE_MODELS:
52
+ raise ValueError(f"Invalid model: {model}. Choose from: {self.AVAILABLE_MODELS}")
53
+ self.model = model
54
+ return
43
55
 
44
56
  if model not in self.AVAILABLE_MODELS:
45
57
  raise ValueError(f"Invalid model: {model}. Choose from: {self.AVAILABLE_MODELS}")
@@ -573,3 +585,4 @@ if __name__ == "__main__":
573
585
  messages=[{"role": "user", "content": "How manr r in strawberry"}]
574
586
  )
575
587
  print(response.choices[0].message.content)
588
+ print()
@@ -7,8 +7,8 @@ import re
7
7
  from typing import List, Dict, Optional, Union, Generator, Any
8
8
 
9
9
  # Import base classes and utility structures
10
- from .base import OpenAICompatibleProvider, BaseChat, BaseCompletions
11
- from .utils import (
10
+ from webscout.Provider.OPENAI.base import OpenAICompatibleProvider, BaseChat, BaseCompletions
11
+ from webscout.Provider.OPENAI.utils import (
12
12
  ChatCompletionChunk, ChatCompletion, Choice, ChoiceDelta,
13
13
  ChatCompletionMessage, CompletionUsage, count_tokens
14
14
  )
@@ -510,4 +510,15 @@ class ChatGPTClone(OpenAICompatibleProvider):
510
510
  class _ModelList:
511
511
  def list(inner_self):
512
512
  return type(self).AVAILABLE_MODELS
513
- return _ModelList()
513
+ return _ModelList()
514
+ if __name__ == "__main__":
515
+ # Example usage
516
+ client = ChatGPTClone()
517
+ response = client.chat.completions.create(
518
+ model="gpt-4",
519
+ messages=[{"role": "user", "content": "Hello!"}]
520
+ )
521
+ print(response.choices[0].message.content)
522
+ print()
523
+ print("Proxies on instance:", client.proxies)
524
+ print("Proxies on session:", client.session.proxies)