webscout 8.3.2__py3-none-any.whl → 8.3.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 (117) hide show
  1. webscout/AIutel.py +367 -41
  2. webscout/Bard.py +2 -22
  3. webscout/Bing_search.py +1 -2
  4. webscout/Provider/AISEARCH/__init__.py +1 -0
  5. webscout/Provider/AISEARCH/scira_search.py +24 -11
  6. webscout/Provider/AISEARCH/stellar_search.py +132 -0
  7. webscout/Provider/Deepinfra.py +75 -57
  8. webscout/Provider/ExaChat.py +93 -63
  9. webscout/Provider/Flowith.py +1 -1
  10. webscout/Provider/FreeGemini.py +2 -2
  11. webscout/Provider/Gemini.py +3 -10
  12. webscout/Provider/GeminiProxy.py +31 -5
  13. webscout/Provider/HeckAI.py +85 -80
  14. webscout/Provider/Jadve.py +56 -50
  15. webscout/Provider/LambdaChat.py +39 -31
  16. webscout/Provider/MiniMax.py +207 -0
  17. webscout/Provider/Nemotron.py +41 -13
  18. webscout/Provider/Netwrck.py +39 -59
  19. webscout/Provider/OLLAMA.py +8 -9
  20. webscout/Provider/OPENAI/BLACKBOXAI.py +0 -1
  21. webscout/Provider/OPENAI/MiniMax.py +298 -0
  22. webscout/Provider/OPENAI/README.md +31 -30
  23. webscout/Provider/OPENAI/TogetherAI.py +4 -17
  24. webscout/Provider/OPENAI/__init__.py +4 -2
  25. webscout/Provider/OPENAI/autoproxy.py +753 -18
  26. webscout/Provider/OPENAI/base.py +7 -76
  27. webscout/Provider/OPENAI/copilot.py +73 -26
  28. webscout/Provider/OPENAI/deepinfra.py +96 -132
  29. webscout/Provider/OPENAI/exachat.py +9 -5
  30. webscout/Provider/OPENAI/flowith.py +179 -166
  31. webscout/Provider/OPENAI/friendli.py +233 -0
  32. webscout/Provider/OPENAI/monochat.py +329 -0
  33. webscout/Provider/OPENAI/netwrck.py +4 -7
  34. webscout/Provider/OPENAI/pydantic_imports.py +1 -172
  35. webscout/Provider/OPENAI/qodo.py +630 -0
  36. webscout/Provider/OPENAI/scirachat.py +82 -49
  37. webscout/Provider/OPENAI/textpollinations.py +13 -12
  38. webscout/Provider/OPENAI/toolbaz.py +1 -0
  39. webscout/Provider/OPENAI/typegpt.py +4 -4
  40. webscout/Provider/OPENAI/utils.py +19 -42
  41. webscout/Provider/OPENAI/x0gpt.py +14 -2
  42. webscout/Provider/OpenGPT.py +54 -32
  43. webscout/Provider/PI.py +58 -84
  44. webscout/Provider/Qodo.py +454 -0
  45. webscout/Provider/StandardInput.py +32 -13
  46. webscout/Provider/TTI/README.md +9 -9
  47. webscout/Provider/TTI/__init__.py +2 -1
  48. webscout/Provider/TTI/aiarta.py +92 -78
  49. webscout/Provider/TTI/infip.py +212 -0
  50. webscout/Provider/TTI/monochat.py +220 -0
  51. webscout/Provider/TeachAnything.py +11 -3
  52. webscout/Provider/TextPollinationsAI.py +91 -82
  53. webscout/Provider/TogetherAI.py +32 -48
  54. webscout/Provider/Venice.py +37 -46
  55. webscout/Provider/VercelAI.py +27 -24
  56. webscout/Provider/WiseCat.py +35 -35
  57. webscout/Provider/WrDoChat.py +22 -26
  58. webscout/Provider/WritingMate.py +26 -22
  59. webscout/Provider/__init__.py +6 -6
  60. webscout/Provider/copilot.py +58 -61
  61. webscout/Provider/freeaichat.py +64 -55
  62. webscout/Provider/granite.py +48 -57
  63. webscout/Provider/koala.py +51 -39
  64. webscout/Provider/learnfastai.py +49 -64
  65. webscout/Provider/llmchat.py +79 -93
  66. webscout/Provider/llmchatco.py +63 -78
  67. webscout/Provider/monochat.py +275 -0
  68. webscout/Provider/multichat.py +51 -40
  69. webscout/Provider/oivscode.py +1 -1
  70. webscout/Provider/scira_chat.py +257 -104
  71. webscout/Provider/scnet.py +13 -13
  72. webscout/Provider/searchchat.py +13 -13
  73. webscout/Provider/sonus.py +12 -11
  74. webscout/Provider/toolbaz.py +25 -8
  75. webscout/Provider/turboseek.py +41 -42
  76. webscout/Provider/typefully.py +27 -12
  77. webscout/Provider/typegpt.py +43 -48
  78. webscout/Provider/uncovr.py +55 -90
  79. webscout/Provider/x0gpt.py +325 -299
  80. webscout/Provider/yep.py +79 -96
  81. webscout/__init__.py +7 -2
  82. webscout/auth/__init__.py +12 -1
  83. webscout/auth/providers.py +27 -5
  84. webscout/auth/routes.py +146 -105
  85. webscout/auth/server.py +367 -312
  86. webscout/client.py +121 -116
  87. webscout/litagent/Readme.md +68 -55
  88. webscout/litagent/agent.py +99 -9
  89. webscout/version.py +1 -1
  90. {webscout-8.3.2.dist-info → webscout-8.3.4.dist-info}/METADATA +102 -91
  91. {webscout-8.3.2.dist-info → webscout-8.3.4.dist-info}/RECORD +95 -107
  92. webscout/Provider/AI21.py +0 -177
  93. webscout/Provider/HuggingFaceChat.py +0 -469
  94. webscout/Provider/OPENAI/freeaichat.py +0 -363
  95. webscout/Provider/TTI/fastflux.py +0 -233
  96. webscout/Provider/Writecream.py +0 -246
  97. webscout/auth/static/favicon.svg +0 -11
  98. webscout/auth/swagger_ui.py +0 -203
  99. webscout/auth/templates/components/authentication.html +0 -237
  100. webscout/auth/templates/components/base.html +0 -103
  101. webscout/auth/templates/components/endpoints.html +0 -750
  102. webscout/auth/templates/components/examples.html +0 -491
  103. webscout/auth/templates/components/footer.html +0 -75
  104. webscout/auth/templates/components/header.html +0 -27
  105. webscout/auth/templates/components/models.html +0 -286
  106. webscout/auth/templates/components/navigation.html +0 -70
  107. webscout/auth/templates/static/api.js +0 -455
  108. webscout/auth/templates/static/icons.js +0 -168
  109. webscout/auth/templates/static/main.js +0 -784
  110. webscout/auth/templates/static/particles.js +0 -201
  111. webscout/auth/templates/static/styles.css +0 -3353
  112. webscout/auth/templates/static/ui.js +0 -374
  113. webscout/auth/templates/swagger_ui.html +0 -170
  114. {webscout-8.3.2.dist-info → webscout-8.3.4.dist-info}/WHEEL +0 -0
  115. {webscout-8.3.2.dist-info → webscout-8.3.4.dist-info}/entry_points.txt +0 -0
  116. {webscout-8.3.2.dist-info → webscout-8.3.4.dist-info}/licenses/LICENSE.md +0 -0
  117. {webscout-8.3.2.dist-info → webscout-8.3.4.dist-info}/top_level.txt +0 -0
webscout/auth/server.py CHANGED
@@ -1,312 +1,367 @@
1
- """
2
- Webscout OpenAI-Compatible API Server
3
-
4
- A FastAPI-based server that provides OpenAI-compatible endpoints for various LLM providers.
5
- Supports streaming and non-streaming chat completions with comprehensive error handling,
6
- authentication, and provider management.
7
- """
8
-
9
- from __future__ import annotations
10
-
11
- import os
12
- import sys
13
- import time
14
- from datetime import datetime, timezone
15
- from typing import Optional
16
-
17
- import uvicorn
18
- from fastapi import FastAPI
19
- from fastapi.middleware.cors import CORSMiddleware
20
- from fastapi.responses import RedirectResponse
21
-
22
- from webscout.Litlogger import Logger, LogLevel, LogFormat, ConsoleHandler
23
- from webscout.auth.swagger_ui import CustomSwaggerUI
24
- from .config import ServerConfig, AppConfig
25
- from .routes import Api
26
- from .providers import initialize_provider_map, initialize_tti_provider_map
27
- from .auth_system import initialize_auth_system
28
-
29
- # Configuration constants
30
- DEFAULT_PORT = 8000
31
- DEFAULT_HOST = "0.0.0.0"
32
- API_VERSION = "v1"
33
-
34
- # Setup Litlogger
35
- logger = Logger(
36
- name="webscout.api",
37
- level=LogLevel.INFO,
38
- handlers=[ConsoleHandler(stream=sys.stdout)],
39
- fmt=LogFormat.DEFAULT
40
- )
41
-
42
- # Global configuration instance
43
- config = ServerConfig()
44
-
45
-
46
- def create_app():
47
- """Create and configure the FastAPI application."""
48
- import os
49
- app_title = os.getenv("WEBSCOUT_API_TITLE", "Webscout OpenAI API")
50
- app_description = os.getenv("WEBSCOUT_API_DESCRIPTION", "OpenAI API compatible interface for various LLM providers with enhanced authentication")
51
- app_version = os.getenv("WEBSCOUT_API_VERSION", "0.2.0")
52
- app_docs_url = os.getenv("WEBSCOUT_API_DOCS_URL", "/docs")
53
- app_redoc_url = os.getenv("WEBSCOUT_API_REDOC_URL", "/redoc")
54
- app_openapi_url = os.getenv("WEBSCOUT_API_OPENAPI_URL", "/openapi.json")
55
-
56
- app = FastAPI(
57
- title=app_title,
58
- description=app_description,
59
- version=app_version,
60
- docs_url=None, # Disable default docs
61
- redoc_url=app_redoc_url,
62
- openapi_url=app_openapi_url,
63
- )
64
-
65
- # Add CORS middleware
66
- app.add_middleware(
67
- CORSMiddleware,
68
- allow_origins=["*"],
69
- allow_credentials=True,
70
- allow_methods=["*"],
71
- allow_headers=["*"],
72
- )
73
-
74
- # Initialize authentication system with configuration
75
- initialize_auth_system(
76
- app,
77
- auth_required=AppConfig.auth_required,
78
- rate_limit_enabled=AppConfig.rate_limit_enabled
79
- )
80
- # Use custom Swagger UI (disable default docs)
81
- app.docs_url = None
82
- app.redoc_url = "/redoc" # keep ReDoc if desired
83
- app.openapi_url = "/openapi.json"
84
- CustomSwaggerUI(app)
85
- # Add startup event handler
86
- @app.on_event("startup")
87
- async def startup():
88
- if hasattr(app.state, 'startup_event'):
89
- await app.state.startup_event()
90
-
91
- # Initialize API routes
92
- api = Api(app)
93
- api.register_validation_exception_handler()
94
- api.register_routes()
95
-
96
- # Initialize providers
97
- initialize_provider_map()
98
- initialize_tti_provider_map()
99
-
100
- # Root redirect
101
- @app.get("/", include_in_schema=False)
102
- async def root():
103
- return RedirectResponse(url="/docs")
104
-
105
- return app
106
-
107
-
108
- def create_app_debug():
109
- """Create app in debug mode."""
110
- return create_app()
111
-
112
-
113
- def start_server(
114
- port: int = DEFAULT_PORT,
115
- host: str = DEFAULT_HOST,
116
- api_key: str = None,
117
- default_provider: str = None,
118
- base_url: str = None,
119
- workers: int = 1,
120
- log_level: str = 'info',
121
- debug: bool = False,
122
- no_auth: bool = False,
123
- no_rate_limit: bool = False
124
- ):
125
- """Start the API server with the given configuration."""
126
- run_api(
127
- host=host,
128
- port=port,
129
- api_key=api_key,
130
- default_provider=default_provider,
131
- base_url=base_url,
132
- workers=workers,
133
- log_level=log_level,
134
- debug=debug,
135
- no_auth=no_auth,
136
- no_rate_limit=no_rate_limit,
137
- )
138
-
139
-
140
- def run_api(
141
- host: str = '0.0.0.0',
142
- port: int = None,
143
- api_key: str = None,
144
- default_provider: str = None,
145
- base_url: str = None,
146
- debug: bool = False,
147
- workers: int = 1,
148
- log_level: str = 'info',
149
- show_available_providers: bool = True,
150
- no_auth: bool = False,
151
- no_rate_limit: bool = False,
152
- ) -> None:
153
- """Run the API server with configuration."""
154
- print("Starting Webscout OpenAI API server...")
155
- if port is None:
156
- port = DEFAULT_PORT
157
-
158
- AppConfig.set_config(
159
- api_key=api_key,
160
- default_provider=default_provider or AppConfig.default_provider,
161
- base_url=base_url,
162
- auth_required=not no_auth,
163
- rate_limit_enabled=not no_rate_limit
164
- )
165
-
166
- if show_available_providers:
167
- if not AppConfig.provider_map:
168
- initialize_provider_map()
169
- if not AppConfig.tti_provider_map:
170
- initialize_tti_provider_map()
171
-
172
- print("\n=== Webscout OpenAI API Server ===")
173
- print(f"Server URL: http://{host if host != '0.0.0.0' else 'localhost'}:{port}")
174
- if AppConfig.base_url:
175
- print(f"Base Path: {AppConfig.base_url}")
176
- api_endpoint_base = f"http://{host if host != '0.0.0.0' else 'localhost'}:{port}{AppConfig.base_url}"
177
- else:
178
- api_endpoint_base = f"http://{host if host != '0.0.0.0' else 'localhost'}:{port}"
179
-
180
- print(f"API Endpoint: {api_endpoint_base}/v1/chat/completions")
181
- print(f"Docs URL: {api_endpoint_base}/docs")
182
-
183
- # Show authentication status
184
- if no_auth:
185
- print(f"Authentication: 🔓 DISABLED (No-Auth Mode)")
186
- elif api_key:
187
- print(f"Authentication: 🔐 Legacy API Key")
188
- else:
189
- print(f"Authentication: 🔑 Enhanced API Keys")
190
-
191
- # Show rate limiting status
192
- if no_rate_limit:
193
- print(f"Rate Limiting: ⚡ DISABLED")
194
- else:
195
- print(f"Rate Limiting: 🛡️ Enabled ({AppConfig.default_rate_limit}/min)")
196
-
197
- print(f"Default Provider: {AppConfig.default_provider}")
198
- print(f"Workers: {workers}")
199
- print(f"Log Level: {log_level}")
200
- print(f"Debug Mode: {'Enabled' if debug else 'Disabled'}")
201
-
202
- providers = list(set(v.__name__ for v in AppConfig.provider_map.values()))
203
- print(f"\n--- Available Providers ({len(providers)}) ---")
204
- for i, provider_name in enumerate(sorted(providers), 1):
205
- print(f"{i}. {provider_name}")
206
-
207
- provider_class_names = set(v.__name__ for v in AppConfig.provider_map.values())
208
- models = sorted([model for model in AppConfig.provider_map.keys() if model not in provider_class_names])
209
- if models:
210
- print(f"\n--- Available Models ({len(models)}) ---")
211
- for i, model_name in enumerate(models, 1):
212
- print(f"{i}. {model_name} (via {AppConfig.provider_map[model_name].__name__})")
213
- else:
214
- print("\nNo specific models registered. Use provider names as models.")
215
-
216
- tti_providers = list(set(v.__name__ for v in AppConfig.tti_provider_map.values()))
217
- print(f"\n--- Available TTI Providers ({len(tti_providers)}) ---")
218
- for i, provider_name in enumerate(sorted(tti_providers), 1):
219
- print(f"{i}. {provider_name}")
220
-
221
- tti_models = sorted([model for model in AppConfig.tti_provider_map.keys() if model not in tti_providers])
222
- if tti_models:
223
- print(f"\n--- Available TTI Models ({len(tti_models)}) ---")
224
- for i, model_name in enumerate(tti_models, 1):
225
- print(f"{i}. {model_name} (via {AppConfig.tti_provider_map[model_name].__name__})")
226
- else:
227
- print("\nNo specific TTI models registered. Use TTI provider names as models.")
228
-
229
- print("\nUse Ctrl+C to stop the server.")
230
- print("=" * 40 + "\n")
231
-
232
- uvicorn_app_str = "webscout.auth.server:create_app_debug" if debug else "webscout.auth.server:create_app"
233
-
234
- # Configure uvicorn settings
235
- uvicorn_config = {
236
- "app": uvicorn_app_str,
237
- "host": host,
238
- "port": int(port),
239
- "factory": True,
240
- "reload": debug,
241
- "log_level": log_level.lower() if log_level else ("debug" if debug else "info"),
242
- }
243
-
244
- # Add workers only if not in debug mode
245
- if not debug and workers > 1:
246
- uvicorn_config["workers"] = workers
247
- print(f"Starting with {workers} workers...")
248
- elif debug:
249
- print("Debug mode enabled - using single worker with reload...")
250
-
251
- uvicorn.run(**uvicorn_config)
252
-
253
-
254
- def main():
255
- """Main entry point for the webscout-server console script."""
256
- import argparse
257
-
258
- # Read environment variables with fallbacks
259
- default_port = int(os.getenv('WEBSCOUT_PORT', os.getenv('PORT', DEFAULT_PORT)))
260
- default_host = os.getenv('WEBSCOUT_HOST', DEFAULT_HOST)
261
- default_workers = int(os.getenv('WEBSCOUT_WORKERS', '1'))
262
- default_log_level = os.getenv('WEBSCOUT_LOG_LEVEL', 'info')
263
- default_api_key = os.getenv('WEBSCOUT_API_KEY', os.getenv('API_KEY'))
264
- default_provider = os.getenv('WEBSCOUT_DEFAULT_PROVIDER', os.getenv('DEFAULT_PROVIDER'))
265
- default_base_url = os.getenv('WEBSCOUT_BASE_URL', os.getenv('BASE_URL'))
266
- default_debug = os.getenv('WEBSCOUT_DEBUG', os.getenv('DEBUG', 'false')).lower() == 'true'
267
- default_no_auth = os.getenv('WEBSCOUT_NO_AUTH', 'false').lower() == 'true'
268
- default_no_rate_limit = os.getenv('WEBSCOUT_NO_RATE_LIMIT', 'false').lower() == 'true'
269
-
270
- parser = argparse.ArgumentParser(description='Start Webscout OpenAI-compatible API server')
271
- parser.add_argument('--port', type=int, default=default_port, help=f'Port to run the server on (default: {default_port})')
272
- parser.add_argument('--host', type=str, default=default_host, help=f'Host to bind the server to (default: {default_host})')
273
- parser.add_argument('--workers', type=int, default=default_workers, help=f'Number of worker processes (default: {default_workers})')
274
- parser.add_argument('--log-level', type=str, default=default_log_level, choices=['debug', 'info', 'warning', 'error', 'critical'], help=f'Log level (default: {default_log_level})')
275
- parser.add_argument('--api-key', type=str, default=default_api_key, help='API key for authentication (optional)')
276
- parser.add_argument('--default-provider', type=str, default=default_provider, help='Default provider to use (optional)')
277
- parser.add_argument('--base-url', type=str, default=default_base_url, help='Base URL for the API (optional, e.g., /api/v1)')
278
- parser.add_argument('--debug', action='store_true', default=default_debug, help='Run in debug mode')
279
- parser.add_argument('--no-auth', action='store_true', default=default_no_auth, help='Disable authentication (no API keys required)')
280
- parser.add_argument('--no-rate-limit', action='store_true', default=default_no_rate_limit, help='Disable rate limiting (unlimited requests)')
281
- args = parser.parse_args()
282
-
283
- # Print configuration summary
284
- print(f"Configuration:")
285
- print(f" Host: {args.host}")
286
- print(f" Port: {args.port}")
287
- print(f" Workers: {args.workers}")
288
- print(f" Log Level: {args.log_level}")
289
- print(f" Debug Mode: {args.debug}")
290
- print(f" No-Auth Mode: {'🔓 ENABLED' if args.no_auth else '🔐 Disabled'}")
291
- print(f" No Rate Limit: {'⚡ ENABLED' if args.no_rate_limit else '🛡️ Disabled'}")
292
- print(f" API Key: {'Set' if args.api_key else 'Not set'}")
293
- print(f" Default Provider: {args.default_provider or 'Not set'}")
294
- print(f" Base URL: {args.base_url or 'Not set'}")
295
- print()
296
-
297
- run_api(
298
- host=args.host,
299
- port=args.port,
300
- workers=args.workers,
301
- log_level=args.log_level,
302
- api_key=args.api_key,
303
- default_provider=args.default_provider,
304
- base_url=args.base_url,
305
- debug=args.debug,
306
- no_auth=args.no_auth,
307
- no_rate_limit=args.no_rate_limit
308
- )
309
-
310
-
311
- if __name__ == "__main__":
312
- main()
1
+ """
2
+ Webscout OpenAI-Compatible API Server
3
+
4
+ A FastAPI-based server that provides OpenAI-compatible endpoints for various LLM providers.
5
+ Supports streaming and non-streaming chat completions with comprehensive error handling,
6
+ authentication, and provider management.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import os
12
+ import sys
13
+ import time
14
+ from datetime import datetime, timezone
15
+ from typing import Optional
16
+
17
+ import uvicorn
18
+ from fastapi import FastAPI
19
+ from fastapi.middleware.cors import CORSMiddleware
20
+ from fastapi.responses import RedirectResponse
21
+ from fastapi.openapi.docs import get_swagger_ui_html
22
+ from starlette.responses import HTMLResponse
23
+
24
+ from webscout.Litlogger import Logger, LogLevel, LogFormat, ConsoleHandler
25
+ from .config import ServerConfig, AppConfig
26
+ from .routes import Api
27
+ from .providers import initialize_provider_map, initialize_tti_provider_map
28
+ from .auth_system import initialize_auth_system
29
+
30
+ # Configuration constants
31
+ DEFAULT_PORT = 8000
32
+ DEFAULT_HOST = "0.0.0.0"
33
+ API_VERSION = "v1"
34
+
35
+ # Setup Litlogger
36
+ logger = Logger(
37
+ name="webscout.api",
38
+ level=LogLevel.INFO,
39
+ handlers=[ConsoleHandler(stream=sys.stdout)],
40
+ fmt=LogFormat.DEFAULT
41
+ )
42
+
43
+ # Global configuration instance
44
+ config = ServerConfig()
45
+
46
+
47
+ def create_app():
48
+ """Create and configure the FastAPI application."""
49
+ import os
50
+ app_title = os.getenv("WEBSCOUT_API_TITLE", "Webscout API")
51
+ app_description = os.getenv("WEBSCOUT_API_DESCRIPTION", "OpenAI API compatible interface for various LLM providers")
52
+ app_version = os.getenv("WEBSCOUT_API_VERSION", "0.2.0")
53
+ app_docs_url = os.getenv("WEBSCOUT_API_DOCS_URL", "/docs")
54
+ app_redoc_url = os.getenv("WEBSCOUT_API_REDOC_URL", "/redoc")
55
+ app_openapi_url = os.getenv("WEBSCOUT_API_OPENAPI_URL", "/openapi.json")
56
+
57
+ app = FastAPI(
58
+ title=app_title,
59
+ description=app_description,
60
+ version=app_version,
61
+ docs_url=None, # Disable default docs
62
+ redoc_url=app_redoc_url,
63
+ openapi_url=app_openapi_url,
64
+ )
65
+
66
+ # Simple Custom Swagger UI with WebScout footer
67
+ @app.get(app_docs_url, include_in_schema=False)
68
+ async def custom_swagger_ui_html():
69
+ html = get_swagger_ui_html(
70
+ openapi_url=app.openapi_url,
71
+ title=app.title + " - API Documentation",
72
+ ).body.decode("utf-8")
73
+
74
+ # Simple, clean footer with WebScout branding
75
+ footer_html = """
76
+ <div style='
77
+ position: fixed;
78
+ bottom: 0;
79
+ left: 0;
80
+ right: 0;
81
+ background: rgba(255, 255, 255, 0.95);
82
+ backdrop-filter: blur(10px);
83
+ border-top: 1px solid #e5e7eb;
84
+ padding: 12px 20px;
85
+ text-align: center;
86
+ font-size: 14px;
87
+ color: #6b7280;
88
+ z-index: 1000;
89
+ box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
90
+ '>
91
+ Powered by <a href='https://github.com/OEvortex/Webscout' target='_blank' style='
92
+ color: #6366f1;
93
+ text-decoration: none;
94
+ font-weight: 600;
95
+ transition: color 0.2s ease;
96
+ ' onmouseover='this.style.color="#4f46e5"' onmouseout='this.style.color="#6366f1"'>
97
+ WebScout
98
+ </a>
99
+ </div>
100
+ <style>
101
+ body {
102
+ padding-bottom: 60px !important;
103
+ }
104
+ .swagger-ui .topbar {
105
+ background-color: #fafafa;
106
+ border-bottom: 1px solid #e5e7eb;
107
+ }
108
+ .swagger-ui .info .title {
109
+ color: #1f2937;
110
+ }
111
+ .swagger-ui .btn.authorize {
112
+ background-color: #6366f1;
113
+ border-color: #6366f1;
114
+ }
115
+ .swagger-ui .btn.authorize:hover {
116
+ background-color: #4f46e5;
117
+ border-color: #4f46e5;
118
+ }
119
+ </style>
120
+ """
121
+
122
+ html = html.replace("</body>", f"{footer_html}</body>")
123
+ return HTMLResponse(content=html)
124
+
125
+ # Add CORS middleware
126
+ app.add_middleware(
127
+ CORSMiddleware,
128
+ allow_origins=["*"],
129
+ allow_credentials=True,
130
+ allow_methods=["*"],
131
+ allow_headers=["*"],
132
+ )
133
+
134
+ # Initialize authentication system with configuration
135
+ initialize_auth_system(
136
+ app,
137
+ auth_required=AppConfig.auth_required,
138
+ rate_limit_enabled=AppConfig.rate_limit_enabled
139
+ )
140
+ # Add startup event handler
141
+ @app.on_event("startup")
142
+ async def startup():
143
+ if hasattr(app.state, 'startup_event'):
144
+ await app.state.startup_event()
145
+
146
+ # Initialize API routes
147
+ api = Api(app)
148
+ api.register_validation_exception_handler()
149
+ api.register_routes()
150
+
151
+ # Initialize providers
152
+ initialize_provider_map()
153
+ initialize_tti_provider_map()
154
+
155
+ # Root redirect
156
+ @app.get("/", include_in_schema=False)
157
+ async def root():
158
+ return RedirectResponse(url="/docs")
159
+
160
+ return app
161
+
162
+
163
+ def create_app_debug():
164
+ """Create app in debug mode."""
165
+ return create_app()
166
+
167
+
168
+ def start_server(
169
+ port: int = DEFAULT_PORT,
170
+ host: str = DEFAULT_HOST,
171
+ api_key: str = None,
172
+ default_provider: str = None,
173
+ base_url: str = None,
174
+ workers: int = 1,
175
+ log_level: str = 'info',
176
+ debug: bool = False,
177
+ no_auth: bool = False,
178
+ no_rate_limit: bool = False
179
+ ):
180
+ """Start the API server with the given configuration."""
181
+ run_api(
182
+ host=host,
183
+ port=port,
184
+ api_key=api_key,
185
+ default_provider=default_provider,
186
+ base_url=base_url,
187
+ workers=workers,
188
+ log_level=log_level,
189
+ debug=debug,
190
+ no_auth=no_auth,
191
+ no_rate_limit=no_rate_limit,
192
+ )
193
+
194
+
195
+ def run_api(
196
+ host: str = '0.0.0.0',
197
+ port: int = None,
198
+ api_key: str = None,
199
+ default_provider: str = None,
200
+ base_url: str = None,
201
+ debug: bool = False,
202
+ workers: int = 1,
203
+ log_level: str = 'info',
204
+ show_available_providers: bool = True,
205
+ no_auth: bool = False,
206
+ no_rate_limit: bool = False,
207
+ ) -> None:
208
+ """Run the API server with configuration."""
209
+ print("Starting Webscout OpenAI API server...")
210
+ if port is None:
211
+ port = DEFAULT_PORT
212
+
213
+ AppConfig.set_config(
214
+ api_key=api_key,
215
+ default_provider=default_provider or AppConfig.default_provider,
216
+ base_url=base_url,
217
+ auth_required=not no_auth,
218
+ rate_limit_enabled=not no_rate_limit
219
+ )
220
+
221
+ if show_available_providers:
222
+ if not AppConfig.provider_map:
223
+ initialize_provider_map()
224
+ if not AppConfig.tti_provider_map:
225
+ initialize_tti_provider_map()
226
+
227
+ print("\n=== Webscout OpenAI API Server ===")
228
+ print(f"Server URL: http://{host if host != '0.0.0.0' else 'localhost'}:{port}")
229
+ if AppConfig.base_url:
230
+ print(f"Base Path: {AppConfig.base_url}")
231
+ api_endpoint_base = f"http://{host if host != '0.0.0.0' else 'localhost'}:{port}{AppConfig.base_url}"
232
+ else:
233
+ api_endpoint_base = f"http://{host if host != '0.0.0.0' else 'localhost'}:{port}"
234
+
235
+ print(f"API Endpoint: {api_endpoint_base}/v1/chat/completions")
236
+ print(f"Docs URL: {api_endpoint_base}/docs")
237
+
238
+ # Show authentication status
239
+ if no_auth:
240
+ print(f"Authentication: 🔓 DISABLED (No-Auth Mode)")
241
+ elif api_key:
242
+ print(f"Authentication: 🔐 Legacy API Key")
243
+ else:
244
+ print(f"Authentication: 🔑 Enhanced API Keys")
245
+
246
+ # Show rate limiting status
247
+ if no_rate_limit:
248
+ print(f"Rate Limiting: ⚡ DISABLED")
249
+ else:
250
+ print(f"Rate Limiting: 🛡️ Enabled ({AppConfig.default_rate_limit}/min)")
251
+
252
+ print(f"Default Provider: {AppConfig.default_provider}")
253
+ print(f"Workers: {workers}")
254
+ print(f"Log Level: {log_level}")
255
+ print(f"Debug Mode: {'Enabled' if debug else 'Disabled'}")
256
+
257
+ providers = list(set(v.__name__ for v in AppConfig.provider_map.values()))
258
+ print(f"\n--- Available Providers ({len(providers)}) ---")
259
+ for i, provider_name in enumerate(sorted(providers), 1):
260
+ print(f"{i}. {provider_name}")
261
+
262
+ provider_class_names = set(v.__name__ for v in AppConfig.provider_map.values())
263
+ models = sorted([model for model in AppConfig.provider_map.keys() if model not in provider_class_names])
264
+ if models:
265
+ print(f"\n--- Available Models ({len(models)}) ---")
266
+ for i, model_name in enumerate(models, 1):
267
+ print(f"{i}. {model_name} (via {AppConfig.provider_map[model_name].__name__})")
268
+ else:
269
+ print("\nNo specific models registered. Use provider names as models.")
270
+
271
+ tti_providers = list(set(v.__name__ for v in AppConfig.tti_provider_map.values()))
272
+ print(f"\n--- Available TTI Providers ({len(tti_providers)}) ---")
273
+ for i, provider_name in enumerate(sorted(tti_providers), 1):
274
+ print(f"{i}. {provider_name}")
275
+
276
+ tti_models = sorted([model for model in AppConfig.tti_provider_map.keys() if model not in tti_providers])
277
+ if tti_models:
278
+ print(f"\n--- Available TTI Models ({len(tti_models)}) ---")
279
+ for i, model_name in enumerate(tti_models, 1):
280
+ print(f"{i}. {model_name} (via {AppConfig.tti_provider_map[model_name].__name__})")
281
+ else:
282
+ print("\nNo specific TTI models registered. Use TTI provider names as models.")
283
+
284
+ print("\nUse Ctrl+C to stop the server.")
285
+ print("=" * 40 + "\n")
286
+
287
+ uvicorn_app_str = "webscout.auth.server:create_app_debug" if debug else "webscout.auth.server:create_app"
288
+
289
+ # Configure uvicorn settings
290
+ uvicorn_config = {
291
+ "app": uvicorn_app_str,
292
+ "host": host,
293
+ "port": int(port),
294
+ "factory": True,
295
+ "reload": debug,
296
+ "log_level": log_level.lower() if log_level else ("debug" if debug else "info"),
297
+ }
298
+
299
+ # Add workers only if not in debug mode
300
+ if not debug and workers > 1:
301
+ uvicorn_config["workers"] = workers
302
+ print(f"Starting with {workers} workers...")
303
+ elif debug:
304
+ print("Debug mode enabled - using single worker with reload...")
305
+
306
+ uvicorn.run(**uvicorn_config)
307
+
308
+
309
+ def main():
310
+ """Main entry point for the webscout-server console script."""
311
+ import argparse
312
+
313
+ # Read environment variables with fallbacks
314
+ default_port = int(os.getenv('WEBSCOUT_PORT', os.getenv('PORT', DEFAULT_PORT)))
315
+ default_host = os.getenv('WEBSCOUT_HOST', DEFAULT_HOST)
316
+ default_workers = int(os.getenv('WEBSCOUT_WORKERS', '1'))
317
+ default_log_level = os.getenv('WEBSCOUT_LOG_LEVEL', 'info')
318
+ default_api_key = os.getenv('WEBSCOUT_API_KEY', os.getenv('API_KEY'))
319
+ default_provider = os.getenv('WEBSCOUT_DEFAULT_PROVIDER', os.getenv('DEFAULT_PROVIDER'))
320
+ default_base_url = os.getenv('WEBSCOUT_BASE_URL', os.getenv('BASE_URL'))
321
+ default_debug = os.getenv('WEBSCOUT_DEBUG', os.getenv('DEBUG', 'false')).lower() == 'true'
322
+ default_no_auth = os.getenv('WEBSCOUT_NO_AUTH', 'false').lower() == 'true'
323
+ default_no_rate_limit = os.getenv('WEBSCOUT_NO_RATE_LIMIT', 'false').lower() == 'true'
324
+
325
+ parser = argparse.ArgumentParser(description='Start Webscout OpenAI-compatible API server')
326
+ parser.add_argument('--port', type=int, default=default_port, help=f'Port to run the server on (default: {default_port})')
327
+ parser.add_argument('--host', type=str, default=default_host, help=f'Host to bind the server to (default: {default_host})')
328
+ parser.add_argument('--workers', type=int, default=default_workers, help=f'Number of worker processes (default: {default_workers})')
329
+ parser.add_argument('--log-level', type=str, default=default_log_level, choices=['debug', 'info', 'warning', 'error', 'critical'], help=f'Log level (default: {default_log_level})')
330
+ parser.add_argument('--api-key', type=str, default=default_api_key, help='API key for authentication (optional)')
331
+ parser.add_argument('--default-provider', type=str, default=default_provider, help='Default provider to use (optional)')
332
+ parser.add_argument('--base-url', type=str, default=default_base_url, help='Base URL for the API (optional, e.g., /api/v1)')
333
+ parser.add_argument('--debug', action='store_true', default=default_debug, help='Run in debug mode')
334
+ parser.add_argument('--no-auth', action='store_true', default=default_no_auth, help='Disable authentication (no API keys required)')
335
+ parser.add_argument('--no-rate-limit', action='store_true', default=default_no_rate_limit, help='Disable rate limiting (unlimited requests)')
336
+ args = parser.parse_args()
337
+
338
+ # Print configuration summary
339
+ print(f"Configuration:")
340
+ print(f" Host: {args.host}")
341
+ print(f" Port: {args.port}")
342
+ print(f" Workers: {args.workers}")
343
+ print(f" Log Level: {args.log_level}")
344
+ print(f" Debug Mode: {args.debug}")
345
+ print(f" No-Auth Mode: {'🔓 ENABLED' if args.no_auth else '🔐 Disabled'}")
346
+ print(f" No Rate Limit: {'⚡ ENABLED' if args.no_rate_limit else '🛡️ Disabled'}")
347
+ print(f" API Key: {'Set' if args.api_key else 'Not set'}")
348
+ print(f" Default Provider: {args.default_provider or 'Not set'}")
349
+ print(f" Base URL: {args.base_url or 'Not set'}")
350
+ print()
351
+
352
+ run_api(
353
+ host=args.host,
354
+ port=args.port,
355
+ workers=args.workers,
356
+ log_level=args.log_level,
357
+ api_key=args.api_key,
358
+ default_provider=args.default_provider,
359
+ base_url=args.base_url,
360
+ debug=args.debug,
361
+ no_auth=args.no_auth,
362
+ no_rate_limit=args.no_rate_limit
363
+ )
364
+
365
+
366
+ if __name__ == "__main__":
367
+ main()