webscout 8.3.1__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.
- webscout/AIutel.py +46 -53
- webscout/Bing_search.py +418 -0
- webscout/Extra/gguf.py +706 -177
- webscout/Provider/AISEARCH/genspark_search.py +7 -7
- webscout/Provider/GeminiProxy.py +140 -0
- webscout/Provider/MCPCore.py +78 -75
- webscout/Provider/OPENAI/BLACKBOXAI.py +1 -4
- webscout/Provider/OPENAI/GeminiProxy.py +328 -0
- webscout/Provider/OPENAI/README.md +2 -0
- webscout/Provider/OPENAI/README_AUTOPROXY.md +238 -0
- webscout/Provider/OPENAI/__init__.py +15 -1
- webscout/Provider/OPENAI/autoproxy.py +332 -39
- webscout/Provider/OPENAI/base.py +15 -5
- webscout/Provider/OPENAI/e2b.py +0 -1
- webscout/Provider/OPENAI/mcpcore.py +109 -70
- webscout/Provider/OPENAI/scirachat.py +59 -51
- webscout/Provider/OPENAI/toolbaz.py +2 -9
- webscout/Provider/OPENAI/xenai.py +514 -0
- webscout/Provider/OPENAI/yep.py +8 -2
- webscout/Provider/TTI/__init__.py +1 -0
- webscout/Provider/TTI/bing.py +231 -0
- webscout/Provider/TTS/speechma.py +45 -39
- webscout/Provider/TogetherAI.py +366 -0
- webscout/Provider/XenAI.py +324 -0
- webscout/Provider/__init__.py +8 -3
- webscout/Provider/deepseek_assistant.py +378 -0
- webscout/auth/__init__.py +44 -0
- webscout/auth/api_key_manager.py +189 -0
- webscout/auth/auth_system.py +100 -0
- webscout/auth/config.py +76 -0
- webscout/auth/database.py +400 -0
- webscout/auth/exceptions.py +67 -0
- webscout/auth/middleware.py +248 -0
- webscout/auth/models.py +130 -0
- webscout/auth/providers.py +257 -0
- webscout/auth/rate_limiter.py +254 -0
- webscout/auth/request_models.py +127 -0
- webscout/auth/request_processing.py +226 -0
- webscout/auth/routes.py +526 -0
- webscout/auth/schemas.py +103 -0
- webscout/auth/server.py +312 -0
- webscout/auth/static/favicon.svg +11 -0
- webscout/auth/swagger_ui.py +203 -0
- webscout/auth/templates/components/authentication.html +237 -0
- webscout/auth/templates/components/base.html +103 -0
- webscout/auth/templates/components/endpoints.html +750 -0
- webscout/auth/templates/components/examples.html +491 -0
- webscout/auth/templates/components/footer.html +75 -0
- webscout/auth/templates/components/header.html +27 -0
- webscout/auth/templates/components/models.html +286 -0
- webscout/auth/templates/components/navigation.html +70 -0
- webscout/auth/templates/static/api.js +455 -0
- webscout/auth/templates/static/icons.js +168 -0
- webscout/auth/templates/static/main.js +784 -0
- webscout/auth/templates/static/particles.js +201 -0
- webscout/auth/templates/static/styles.css +3353 -0
- webscout/auth/templates/static/ui.js +374 -0
- webscout/auth/templates/swagger_ui.html +170 -0
- webscout/client.py +49 -3
- webscout/scout/core/scout.py +104 -26
- webscout/scout/element.py +139 -18
- webscout/swiftcli/core/cli.py +14 -3
- webscout/swiftcli/decorators/output.py +59 -9
- webscout/update_checker.py +31 -49
- webscout/version.py +1 -1
- webscout/webscout_search.py +4 -12
- webscout/webscout_search_async.py +3 -10
- webscout/yep_search.py +2 -11
- {webscout-8.3.1.dist-info → webscout-8.3.2.dist-info}/METADATA +41 -11
- {webscout-8.3.1.dist-info → webscout-8.3.2.dist-info}/RECORD +74 -36
- {webscout-8.3.1.dist-info → webscout-8.3.2.dist-info}/entry_points.txt +1 -1
- webscout/Provider/HF_space/__init__.py +0 -0
- webscout/Provider/HF_space/qwen_qwen2.py +0 -206
- webscout/Provider/OPENAI/api.py +0 -1320
- {webscout-8.3.1.dist-info → webscout-8.3.2.dist-info}/WHEEL +0 -0
- {webscout-8.3.1.dist-info → webscout-8.3.2.dist-info}/licenses/LICENSE.md +0 -0
- {webscout-8.3.1.dist-info → webscout-8.3.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebScout API Documentation - API Interaction Functions
|
|
3
|
+
* Handles all API requests and responses
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// API interaction utilities
|
|
7
|
+
window.WebScoutAPI = {
|
|
8
|
+
baseUrl: '',
|
|
9
|
+
|
|
10
|
+
init(baseUrl) {
|
|
11
|
+
this.baseUrl = baseUrl || window.WEBSCOUT_CONFIG?.baseUrl || '';
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
// Generic API request function
|
|
15
|
+
async makeRequest(endpoint, method = 'GET', body = null, requireAuth = true) {
|
|
16
|
+
const url = `${this.baseUrl}${endpoint}`;
|
|
17
|
+
const startTime = Date.now();
|
|
18
|
+
|
|
19
|
+
const headers = {
|
|
20
|
+
'Content-Type': 'application/json'
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
if (requireAuth && WebScoutApp.currentApiKey) {
|
|
24
|
+
headers['Authorization'] = `Bearer ${WebScoutApp.currentApiKey}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const options = {
|
|
28
|
+
method,
|
|
29
|
+
headers
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
if (body && method !== 'GET') {
|
|
33
|
+
options.body = JSON.stringify(body);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
const response = await fetch(url, options);
|
|
38
|
+
const endTime = Date.now();
|
|
39
|
+
const responseTime = endTime - startTime;
|
|
40
|
+
|
|
41
|
+
let data;
|
|
42
|
+
const contentType = response.headers.get('content-type');
|
|
43
|
+
|
|
44
|
+
if (contentType && contentType.includes('application/json')) {
|
|
45
|
+
data = await response.json();
|
|
46
|
+
} else {
|
|
47
|
+
data = await response.text();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
status: response.status,
|
|
52
|
+
statusText: response.statusText,
|
|
53
|
+
data,
|
|
54
|
+
responseTime,
|
|
55
|
+
headers: Object.fromEntries(response.headers.entries())
|
|
56
|
+
};
|
|
57
|
+
} catch (error) {
|
|
58
|
+
return {
|
|
59
|
+
status: 0,
|
|
60
|
+
statusText: 'Network Error',
|
|
61
|
+
data: { error: error.message },
|
|
62
|
+
responseTime: Date.now() - startTime,
|
|
63
|
+
headers: {}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
// Streaming request handler
|
|
69
|
+
async makeStreamingRequest(endpoint, body, onChunk, onComplete, onError) {
|
|
70
|
+
const url = `${this.baseUrl}${endpoint}`;
|
|
71
|
+
|
|
72
|
+
const headers = {
|
|
73
|
+
'Content-Type': 'application/json'
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
if (WebScoutApp.currentApiKey) {
|
|
77
|
+
headers['Authorization'] = `Bearer ${WebScoutApp.currentApiKey}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const response = await fetch(url, {
|
|
82
|
+
method: 'POST',
|
|
83
|
+
headers,
|
|
84
|
+
body: JSON.stringify(body)
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
if (!response.ok) {
|
|
88
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const reader = response.body.getReader();
|
|
92
|
+
const decoder = new TextDecoder();
|
|
93
|
+
|
|
94
|
+
while (true) {
|
|
95
|
+
const { done, value } = await reader.read();
|
|
96
|
+
|
|
97
|
+
if (done) {
|
|
98
|
+
onComplete?.();
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const chunk = decoder.decode(value);
|
|
103
|
+
const lines = chunk.split('\n');
|
|
104
|
+
|
|
105
|
+
for (const line of lines) {
|
|
106
|
+
if (line.startsWith('data: ')) {
|
|
107
|
+
const data = line.slice(6);
|
|
108
|
+
if (data === '[DONE]') {
|
|
109
|
+
onComplete?.();
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
const parsed = JSON.parse(data);
|
|
115
|
+
onChunk?.(parsed);
|
|
116
|
+
} catch (e) {
|
|
117
|
+
// Skip invalid JSON
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
} catch (error) {
|
|
123
|
+
onError?.(error);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// API test functions
|
|
129
|
+
async function testChatCompletion(event) {
|
|
130
|
+
const button = event?.target || event;
|
|
131
|
+
const originalText = showLoading(button);
|
|
132
|
+
|
|
133
|
+
const model = document.getElementById('chat-model').value;
|
|
134
|
+
const messagesText = document.getElementById('chat-messages').value;
|
|
135
|
+
const temperature = parseFloat(document.getElementById('chat-temperature').value);
|
|
136
|
+
const maxTokens = parseInt(document.getElementById('chat-max-tokens').value);
|
|
137
|
+
const stream = document.getElementById('chat-stream').checked;
|
|
138
|
+
|
|
139
|
+
let messages;
|
|
140
|
+
try {
|
|
141
|
+
messages = JSON.parse(messagesText || '[{"role": "user", "content": "Hello!"}]');
|
|
142
|
+
} catch (e) {
|
|
143
|
+
WebScoutApp.showToast('Invalid JSON in messages field', 'error');
|
|
144
|
+
hideLoading(button, originalText);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const body = {
|
|
149
|
+
model,
|
|
150
|
+
messages,
|
|
151
|
+
temperature,
|
|
152
|
+
max_tokens: maxTokens,
|
|
153
|
+
stream
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
if (stream) {
|
|
157
|
+
await handleStreamingChatCompletion(body, button, originalText);
|
|
158
|
+
} else {
|
|
159
|
+
const response = await WebScoutAPI.makeRequest('/v1/chat/completions', 'POST', body);
|
|
160
|
+
displayResponse('chat-response', response);
|
|
161
|
+
hideLoading(button, originalText);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function handleStreamingChatCompletion(body, button, originalText) {
|
|
166
|
+
const responseDiv = document.getElementById('chat-response');
|
|
167
|
+
responseDiv.innerHTML = '';
|
|
168
|
+
responseDiv.classList.add('show');
|
|
169
|
+
|
|
170
|
+
const statusDiv = document.createElement('div');
|
|
171
|
+
statusDiv.className = 'response-header';
|
|
172
|
+
statusDiv.innerHTML = `
|
|
173
|
+
<div class="response-status">
|
|
174
|
+
<span class="status-code status-200">STREAMING</span>
|
|
175
|
+
<span class="response-time">Connecting...</span>
|
|
176
|
+
</div>
|
|
177
|
+
`;
|
|
178
|
+
responseDiv.appendChild(statusDiv);
|
|
179
|
+
|
|
180
|
+
const contentDiv = document.createElement('div');
|
|
181
|
+
contentDiv.className = 'response-body';
|
|
182
|
+
const preElement = document.createElement('pre');
|
|
183
|
+
contentDiv.appendChild(preElement);
|
|
184
|
+
responseDiv.appendChild(contentDiv);
|
|
185
|
+
|
|
186
|
+
let fullContent = '';
|
|
187
|
+
const startTime = Date.now();
|
|
188
|
+
|
|
189
|
+
await WebScoutAPI.makeStreamingRequest(
|
|
190
|
+
'/v1/chat/completions',
|
|
191
|
+
body,
|
|
192
|
+
(chunk) => {
|
|
193
|
+
if (chunk.choices && chunk.choices[0]?.delta?.content) {
|
|
194
|
+
const content = chunk.choices[0].delta.content;
|
|
195
|
+
fullContent += content;
|
|
196
|
+
preElement.textContent = fullContent;
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
() => {
|
|
200
|
+
const endTime = Date.now();
|
|
201
|
+
statusDiv.querySelector('.response-time').textContent = `${endTime - startTime}ms`;
|
|
202
|
+
hideLoading(button, originalText);
|
|
203
|
+
},
|
|
204
|
+
(error) => {
|
|
205
|
+
preElement.textContent = `Error: ${error.message}`;
|
|
206
|
+
statusDiv.querySelector('.status-code').className = 'status-code status-500';
|
|
207
|
+
statusDiv.querySelector('.status-code').textContent = 'ERROR';
|
|
208
|
+
hideLoading(button, originalText);
|
|
209
|
+
}
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async function testImageGeneration(event) {
|
|
214
|
+
const button = event?.target || event;
|
|
215
|
+
const originalText = showLoading(button);
|
|
216
|
+
|
|
217
|
+
const prompt = document.getElementById('image-prompt').value;
|
|
218
|
+
const model = document.getElementById('image-model').value;
|
|
219
|
+
const size = document.getElementById('image-size').value;
|
|
220
|
+
const n = parseInt(document.getElementById('image-count').value);
|
|
221
|
+
|
|
222
|
+
if (!prompt.trim()) {
|
|
223
|
+
WebScoutApp.showToast('Please enter a prompt', 'error');
|
|
224
|
+
hideLoading(button, originalText);
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const body = { prompt, model, size, n };
|
|
229
|
+
const response = await WebScoutAPI.makeRequest('/v1/images/generations', 'POST', body);
|
|
230
|
+
|
|
231
|
+
displayImageResponse('image-response', response);
|
|
232
|
+
hideLoading(button, originalText);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
async function testListModels(event) {
|
|
236
|
+
const button = event?.target || event;
|
|
237
|
+
const originalText = showLoading(button);
|
|
238
|
+
|
|
239
|
+
const response = await WebScoutAPI.makeRequest('/v1/models', 'GET');
|
|
240
|
+
displayResponse('models-response', response);
|
|
241
|
+
hideLoading(button, originalText);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async function testListTTIModels(event) {
|
|
245
|
+
const button = event?.target || event;
|
|
246
|
+
const originalText = showLoading(button);
|
|
247
|
+
|
|
248
|
+
const response = await WebScoutAPI.makeRequest('/v1/TTI/models', 'GET');
|
|
249
|
+
displayResponse('tti-models-response', response);
|
|
250
|
+
hideLoading(button, originalText);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
async function testGetModel(event) {
|
|
254
|
+
const button = event?.target || event;
|
|
255
|
+
const originalText = showLoading(button);
|
|
256
|
+
|
|
257
|
+
const modelId = document.getElementById('model-id-input').value;
|
|
258
|
+
|
|
259
|
+
if (!modelId.trim()) {
|
|
260
|
+
WebScoutApp.showToast('Please enter a model ID', 'error');
|
|
261
|
+
hideLoading(button, originalText);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const response = await WebScoutAPI.makeRequest(`/v1/models/${modelId}`, 'GET');
|
|
266
|
+
displayResponse('model-info-response', response);
|
|
267
|
+
hideLoading(button, originalText);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async function testGenerateApiKey(event) {
|
|
271
|
+
const button = event?.target || event;
|
|
272
|
+
const originalText = showLoading(button);
|
|
273
|
+
|
|
274
|
+
const username = document.getElementById('auth-username').value;
|
|
275
|
+
const name = document.getElementById('auth-name').value;
|
|
276
|
+
const rateLimit = parseInt(document.getElementById('auth-rate-limit').value);
|
|
277
|
+
const expiresInDays = parseInt(document.getElementById('auth-expires').value) || null;
|
|
278
|
+
|
|
279
|
+
if (!username.trim()) {
|
|
280
|
+
WebScoutApp.showToast('Please enter a username', 'error');
|
|
281
|
+
hideLoading(button, originalText);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const body = {
|
|
286
|
+
username,
|
|
287
|
+
name: name || null,
|
|
288
|
+
rate_limit: rateLimit,
|
|
289
|
+
expires_in_days: expiresInDays
|
|
290
|
+
};
|
|
291
|
+
const response = await WebScoutAPI.makeRequest('/v1/auth/generate-key', 'POST', body, false);
|
|
292
|
+
|
|
293
|
+
if (response.status === 200 && response.data.api_key) {
|
|
294
|
+
WebScoutApp.currentApiKey = response.data.api_key;
|
|
295
|
+
localStorage.setItem('webscout_api_key', WebScoutApp.currentApiKey);
|
|
296
|
+
WebScoutApp.showToast('API key generated and saved!', 'success');
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
displayResponse('auth-response', response);
|
|
300
|
+
hideLoading(button, originalText);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
async function testValidateApiKey(event) {
|
|
304
|
+
const button = event?.target || event;
|
|
305
|
+
const originalText = showLoading(button);
|
|
306
|
+
|
|
307
|
+
const apiKey = document.getElementById('validate-api-key').value;
|
|
308
|
+
|
|
309
|
+
if (!apiKey.trim()) {
|
|
310
|
+
WebScoutApp.showToast('Please enter an API key', 'error');
|
|
311
|
+
hideLoading(button, originalText);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Temporarily use the provided key for validation
|
|
316
|
+
const originalKey = WebScoutApp.currentApiKey;
|
|
317
|
+
WebScoutApp.currentApiKey = apiKey;
|
|
318
|
+
|
|
319
|
+
const response = await WebScoutAPI.makeRequest('/v1/auth/validate', 'GET');
|
|
320
|
+
|
|
321
|
+
// Restore original key
|
|
322
|
+
WebScoutApp.currentApiKey = originalKey;
|
|
323
|
+
|
|
324
|
+
displayResponse('validate-response', response);
|
|
325
|
+
hideLoading(button, originalText);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
async function testWebSearch(event) {
|
|
329
|
+
const button = event?.target || event;
|
|
330
|
+
const originalText = showLoading(button);
|
|
331
|
+
|
|
332
|
+
const query = document.getElementById('search-query').value;
|
|
333
|
+
const engine = document.getElementById('search-engine').value;
|
|
334
|
+
const type = document.getElementById('search-type').value;
|
|
335
|
+
const maxResults = parseInt(document.getElementById('search-max-results').value);
|
|
336
|
+
const region = document.getElementById('search-region').value;
|
|
337
|
+
const safesearch = document.getElementById('search-safesearch').value;
|
|
338
|
+
|
|
339
|
+
if (!query.trim()) {
|
|
340
|
+
WebScoutApp.showToast('Please enter a search query', 'error');
|
|
341
|
+
hideLoading(button, originalText);
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const params = new URLSearchParams({
|
|
346
|
+
q: query,
|
|
347
|
+
engine,
|
|
348
|
+
type,
|
|
349
|
+
max_results: maxResults,
|
|
350
|
+
region,
|
|
351
|
+
safesearch
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
const response = await WebScoutAPI.makeRequest(`/search?${params}`, 'GET', null, false);
|
|
355
|
+
displayResponse('search-response', response);
|
|
356
|
+
hideLoading(button, originalText);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
async function testHealthCheck(event) {
|
|
360
|
+
const button = event?.target || event;
|
|
361
|
+
const originalText = showLoading(button);
|
|
362
|
+
|
|
363
|
+
const response = await WebScoutAPI.makeRequest('/health', 'GET', null, false);
|
|
364
|
+
displayResponse('health-response', response);
|
|
365
|
+
hideLoading(button, originalText);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Load models for the models tab
|
|
369
|
+
async function loadModels() {
|
|
370
|
+
const container = document.getElementById('models-container');
|
|
371
|
+
const loading = document.getElementById('models-loading');
|
|
372
|
+
const error = document.getElementById('models-error');
|
|
373
|
+
|
|
374
|
+
if (!container) return;
|
|
375
|
+
|
|
376
|
+
// Show loading state
|
|
377
|
+
loading.style.display = 'block';
|
|
378
|
+
error.style.display = 'none';
|
|
379
|
+
container.innerHTML = '';
|
|
380
|
+
|
|
381
|
+
try {
|
|
382
|
+
const response = await WebScoutAPI.makeRequest('/v1/models', 'GET', null, false);
|
|
383
|
+
|
|
384
|
+
if (response.status === 200 && response.data.data) {
|
|
385
|
+
const models = response.data.data;
|
|
386
|
+
displayModels(models);
|
|
387
|
+
updateModelStats(models);
|
|
388
|
+
} else {
|
|
389
|
+
throw new Error('Failed to load models');
|
|
390
|
+
}
|
|
391
|
+
} catch (err) {
|
|
392
|
+
error.style.display = 'block';
|
|
393
|
+
WebScoutApp.showToast('Failed to load models', 'error');
|
|
394
|
+
} finally {
|
|
395
|
+
loading.style.display = 'none';
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function displayModels(models) {
|
|
400
|
+
const container = document.getElementById('models-container');
|
|
401
|
+
if (!container) return;
|
|
402
|
+
|
|
403
|
+
container.innerHTML = models.map(model => `
|
|
404
|
+
<div class="model-card" data-model-type="${model.object}" data-model-name="${model.id.toLowerCase()}">
|
|
405
|
+
<div class="model-header">
|
|
406
|
+
<div>
|
|
407
|
+
<div class="model-name">${model.id}</div>
|
|
408
|
+
<div class="model-provider">by ${model.owned_by}</div>
|
|
409
|
+
</div>
|
|
410
|
+
<div class="model-type">${model.object}</div>
|
|
411
|
+
</div>
|
|
412
|
+
<div class="model-description">
|
|
413
|
+
${getModelDescription(model.id)}
|
|
414
|
+
</div>
|
|
415
|
+
<div class="model-meta">
|
|
416
|
+
<span class="model-created">Created: ${formatTimestamp(model.created * 1000)}</span>
|
|
417
|
+
<span class="model-id">ID: ${model.id}</span>
|
|
418
|
+
</div>
|
|
419
|
+
</div>
|
|
420
|
+
`).join('');
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function getModelDescription(modelId) {
|
|
424
|
+
const descriptions = {
|
|
425
|
+
'gpt-4': 'Most capable GPT model, great for complex tasks requiring deep understanding.',
|
|
426
|
+
'gpt-3.5-turbo': 'Fast and efficient model, perfect for most conversational tasks.',
|
|
427
|
+
'claude-3-sonnet': 'Anthropic\'s balanced model with strong reasoning capabilities.',
|
|
428
|
+
'dall-e-2': 'AI image generation model that creates images from text descriptions.',
|
|
429
|
+
'dall-e-3': 'Latest image generation model with improved quality and prompt adherence.'
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
return descriptions[modelId] || 'Advanced AI model for various tasks and applications.';
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function updateModelStats(models) {
|
|
436
|
+
const totalModels = document.getElementById('total-models');
|
|
437
|
+
const chatModels = document.getElementById('chat-models');
|
|
438
|
+
const imageModels = document.getElementById('image-models');
|
|
439
|
+
const providersCount = document.getElementById('providers-count');
|
|
440
|
+
|
|
441
|
+
if (totalModels) totalModels.textContent = models.length;
|
|
442
|
+
|
|
443
|
+
const chatCount = models.filter(m => m.object === 'model' || m.id.includes('gpt') || m.id.includes('claude')).length;
|
|
444
|
+
const imageCount = models.filter(m => m.id.includes('dall-e') || m.id.includes('stable')).length;
|
|
445
|
+
const providers = new Set(models.map(m => m.owned_by)).size;
|
|
446
|
+
|
|
447
|
+
if (chatModels) chatModels.textContent = chatCount;
|
|
448
|
+
if (imageModels) imageModels.textContent = imageCount;
|
|
449
|
+
if (providersCount) providersCount.textContent = providers;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Initialize API module
|
|
453
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
454
|
+
WebScoutAPI.init();
|
|
455
|
+
});
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modern SVG Icon System for WebScout
|
|
3
|
+
* Replaces emoji icons with scalable, professional SVG icons
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const WebScoutIcons = {
|
|
7
|
+
// API & Technical Icons
|
|
8
|
+
api: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
9
|
+
<path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/>
|
|
10
|
+
</svg>`,
|
|
11
|
+
|
|
12
|
+
rocket: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
13
|
+
<path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z"/>
|
|
14
|
+
<path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z"/>
|
|
15
|
+
<path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0"/>
|
|
16
|
+
<path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5"/>
|
|
17
|
+
</svg>`,
|
|
18
|
+
|
|
19
|
+
shield: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
20
|
+
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>
|
|
21
|
+
</svg>`,
|
|
22
|
+
|
|
23
|
+
bot: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
24
|
+
<rect x="3" y="11" width="18" height="10" rx="2" ry="2"/>
|
|
25
|
+
<circle cx="12" cy="5" r="2"/>
|
|
26
|
+
<path d="M12 7v4"/>
|
|
27
|
+
<line x1="8" y1="16" x2="8" y2="16"/>
|
|
28
|
+
<line x1="16" y1="16" x2="16" y2="16"/>
|
|
29
|
+
</svg>`,
|
|
30
|
+
|
|
31
|
+
code: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
32
|
+
<polyline points="16,18 22,12 16,6"/>
|
|
33
|
+
<polyline points="8,6 2,12 8,18"/>
|
|
34
|
+
</svg>`,
|
|
35
|
+
|
|
36
|
+
database: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
37
|
+
<ellipse cx="12" cy="5" rx="9" ry="3"/>
|
|
38
|
+
<path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/>
|
|
39
|
+
<path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/>
|
|
40
|
+
</svg>`,
|
|
41
|
+
|
|
42
|
+
// Status & UI Icons
|
|
43
|
+
check: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
44
|
+
<polyline points="20,6 9,17 4,12"/>
|
|
45
|
+
</svg>`,
|
|
46
|
+
|
|
47
|
+
x: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
48
|
+
<line x1="18" y1="6" x2="6" y2="18"/>
|
|
49
|
+
<line x1="6" y1="6" x2="18" y2="18"/>
|
|
50
|
+
</svg>`,
|
|
51
|
+
|
|
52
|
+
warning: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
53
|
+
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/>
|
|
54
|
+
<line x1="12" y1="9" x2="12" y2="13"/>
|
|
55
|
+
<line x1="12" y1="17" x2="12.01" y2="17"/>
|
|
56
|
+
</svg>`,
|
|
57
|
+
|
|
58
|
+
info: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
59
|
+
<circle cx="12" cy="12" r="10"/>
|
|
60
|
+
<line x1="12" y1="16" x2="12" y2="12"/>
|
|
61
|
+
<line x1="12" y1="8" x2="12.01" y2="8"/>
|
|
62
|
+
</svg>`,
|
|
63
|
+
|
|
64
|
+
// Navigation Icons
|
|
65
|
+
menu: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
66
|
+
<line x1="3" y1="6" x2="21" y2="6"/>
|
|
67
|
+
<line x1="3" y1="12" x2="21" y2="12"/>
|
|
68
|
+
<line x1="3" y1="18" x2="21" y2="18"/>
|
|
69
|
+
</svg>`,
|
|
70
|
+
|
|
71
|
+
chevronDown: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
72
|
+
<polyline points="6,9 12,15 18,9"/>
|
|
73
|
+
</svg>`,
|
|
74
|
+
|
|
75
|
+
chevronUp: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
76
|
+
<polyline points="18,15 12,9 6,15"/>
|
|
77
|
+
</svg>`,
|
|
78
|
+
|
|
79
|
+
// Action Icons
|
|
80
|
+
copy: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
81
|
+
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"/>
|
|
82
|
+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
|
|
83
|
+
</svg>`,
|
|
84
|
+
|
|
85
|
+
download: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
86
|
+
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
|
|
87
|
+
<polyline points="7,10 12,15 17,10"/>
|
|
88
|
+
<line x1="12" y1="15" x2="12" y2="3"/>
|
|
89
|
+
</svg>`,
|
|
90
|
+
|
|
91
|
+
search: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
92
|
+
<circle cx="11" cy="11" r="8"/>
|
|
93
|
+
<path d="m21 21-4.35-4.35"/>
|
|
94
|
+
</svg>`,
|
|
95
|
+
|
|
96
|
+
// Social Icons
|
|
97
|
+
github: `<svg viewBox="0 0 24 24" fill="currentColor">
|
|
98
|
+
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
|
|
99
|
+
</svg>`,
|
|
100
|
+
|
|
101
|
+
twitter: `<svg viewBox="0 0 24 24" fill="currentColor">
|
|
102
|
+
<path d="M23.953 4.57a10 10 0 01-2.825.775 4.958 4.958 0 002.163-2.723c-.951.555-2.005.959-3.127 1.184a4.92 4.92 0 00-8.384 4.482C7.69 8.095 4.067 6.13 1.64 3.162a4.822 4.822 0 00-.666 2.475c0 1.71.87 3.213 2.188 4.096a4.904 4.904 0 01-2.228-.616v.06a4.923 4.923 0 003.946 4.827 4.996 4.996 0 01-2.212.085 4.936 4.936 0 004.604 3.417 9.867 9.867 0 01-6.102 2.105c-.39 0-.779-.023-1.17-.067a13.995 13.995 0 007.557 2.209c9.053 0 13.998-7.496 13.998-13.985 0-.21 0-.42-.015-.63A9.935 9.935 0 0024 4.59z"/>
|
|
103
|
+
</svg>`,
|
|
104
|
+
|
|
105
|
+
linkedin: `<svg viewBox="0 0 24 24" fill="currentColor">
|
|
106
|
+
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/>
|
|
107
|
+
</svg>`,
|
|
108
|
+
|
|
109
|
+
// Utility function to create icon element
|
|
110
|
+
create: function(iconName, className = '', size = '24') {
|
|
111
|
+
const icon = this[iconName];
|
|
112
|
+
if (!icon) {
|
|
113
|
+
console.warn(`Icon "${iconName}" not found`);
|
|
114
|
+
return '';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const wrapper = document.createElement('span');
|
|
118
|
+
wrapper.className = `icon ${className}`;
|
|
119
|
+
wrapper.style.width = `${size}px`;
|
|
120
|
+
wrapper.style.height = `${size}px`;
|
|
121
|
+
wrapper.style.display = 'inline-block';
|
|
122
|
+
wrapper.innerHTML = icon;
|
|
123
|
+
|
|
124
|
+
return wrapper.outerHTML;
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
// Utility function to replace emoji icons
|
|
128
|
+
replaceEmojis: function() {
|
|
129
|
+
const replacements = {
|
|
130
|
+
'🚀': 'rocket',
|
|
131
|
+
'🔐': 'shield',
|
|
132
|
+
'🤖': 'bot',
|
|
133
|
+
'💡': 'code',
|
|
134
|
+
'📡': 'api',
|
|
135
|
+
'🔌': 'database',
|
|
136
|
+
'⚡': 'api',
|
|
137
|
+
'✅': 'check',
|
|
138
|
+
'❌': 'x',
|
|
139
|
+
'⚠️': 'warning',
|
|
140
|
+
'ℹ️': 'info',
|
|
141
|
+
'▼': 'chevronDown',
|
|
142
|
+
'▲': 'chevronUp',
|
|
143
|
+
'🐙': 'github',
|
|
144
|
+
'🐦': 'twitter',
|
|
145
|
+
'💼': 'linkedin'
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
Object.keys(replacements).forEach(emoji => {
|
|
149
|
+
const elements = document.querySelectorAll(`*:not(script):not(style)`);
|
|
150
|
+
elements.forEach(el => {
|
|
151
|
+
if (el.textContent && el.textContent.includes(emoji)) {
|
|
152
|
+
el.innerHTML = el.innerHTML.replace(
|
|
153
|
+
new RegExp(emoji, 'g'),
|
|
154
|
+
this.create(replacements[emoji], 'emoji-replacement', '20')
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// Auto-replace emojis when DOM is loaded
|
|
163
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
164
|
+
WebScoutIcons.replaceEmojis();
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Export for global use
|
|
168
|
+
window.WebScoutIcons = WebScoutIcons;
|