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
@@ -1,784 +0,0 @@
1
- /**
2
- * WebScout API Documentation - Main JavaScript
3
- * Handles UI interactions, API testing, and dynamic content
4
- */
5
-
6
- // Global application state
7
- window.WebScoutApp = {
8
- config: {},
9
- currentApiKey: '',
10
- activeTab: 'endpoints',
11
-
12
- // Initialize the application
13
- init() {
14
- console.log('WebScout App initializing...');
15
- this.config = window.WEBSCOUT_CONFIG || {};
16
- this.loadSavedApiKey();
17
- this.setupEventListeners();
18
- this.initializePage();
19
- this.initScrollIndicator();
20
- this.hideLoadingScreen();
21
- },
22
-
23
- // Load saved API key from localStorage
24
- loadSavedApiKey() {
25
- const savedApiKey = localStorage.getItem('webscout_api_key');
26
- if (savedApiKey) {
27
- this.currentApiKey = savedApiKey;
28
- }
29
- },
30
-
31
- // Setup all event listeners
32
- setupEventListeners() {
33
- // Tab navigation
34
- document.querySelectorAll('.nav-tab').forEach(tab => {
35
- tab.addEventListener('click', (e) => {
36
- const tabName = e.target.closest('.nav-tab').dataset.tab;
37
- this.showTab(tabName);
38
- });
39
- });
40
-
41
- // Endpoint toggles - add direct event listeners
42
- this.setupEndpointToggles();
43
-
44
- // Example language tabs
45
- document.querySelectorAll('.example-tab').forEach(tab => {
46
- tab.addEventListener('click', (e) => {
47
- const language = e.target.closest('.example-tab').dataset.language;
48
- this.showExampleLanguage(language);
49
- });
50
- });
51
-
52
- // View toggle for models
53
- document.querySelectorAll('.view-btn').forEach(btn => {
54
- btn.addEventListener('click', (e) => {
55
- const view = e.target.closest('.view-btn').dataset.view;
56
- this.toggleModelsView(view);
57
- });
58
- });
59
-
60
- // Search functionality
61
- const searchInput = document.getElementById('model-search');
62
- if (searchInput) {
63
- searchInput.addEventListener('input', (e) => {
64
- this.searchModels(e.target.value);
65
- });
66
- }
67
-
68
- // Filter functionality
69
- const filterSelect = document.getElementById('model-filter');
70
- if (filterSelect) {
71
- filterSelect.addEventListener('change', (e) => {
72
- this.filterModels(e.target.value);
73
- });
74
- }
75
-
76
- // Copy code buttons
77
- document.addEventListener('click', (e) => {
78
- if (e.target.classList.contains('copy-code-btn') || e.target.closest('.copy-code-btn')) {
79
- const btn = e.target.closest('.copy-code-btn');
80
- const codeId = btn.getAttribute('onclick')?.match(/copyCode\('([^']+)'\)/)?.[1];
81
- if (codeId) {
82
- this.copyCode(codeId);
83
- }
84
- }
85
- });
86
- },
87
-
88
- // Setup endpoint toggle functionality
89
- setupEndpointToggles() {
90
- // Wait for DOM to be fully ready
91
- setTimeout(() => {
92
- const headers = document.querySelectorAll('.endpoint-header');
93
- console.log('Setting up endpoint toggles for', headers.length, 'headers');
94
-
95
- headers.forEach((header, index) => {
96
- // Remove any existing listeners
97
- header.replaceWith(header.cloneNode(true));
98
- const newHeader = document.querySelectorAll('.endpoint-header')[index];
99
-
100
- newHeader.addEventListener('click', (e) => {
101
- e.preventDefault();
102
- e.stopPropagation();
103
- console.log(`Endpoint header ${index} clicked`);
104
- this.toggleEndpoint(newHeader);
105
- });
106
-
107
- // Ensure it's clickable
108
- newHeader.style.cursor = 'pointer';
109
- console.log(`Added click handler to header ${index}`);
110
- });
111
- }, 100);
112
- },
113
-
114
- // Initialize page content
115
- initializePage() {
116
- // Update status indicator
117
- this.updateStatusIndicator();
118
-
119
- // Initialize syntax highlighting
120
- if (typeof hljs !== 'undefined') {
121
- hljs.highlightAll();
122
- }
123
-
124
- // Set up periodic status checks
125
- setInterval(() => {
126
- this.updateStatusIndicator();
127
- }, 30000);
128
- },
129
-
130
- // Hide loading screen
131
- hideLoadingScreen() {
132
- setTimeout(() => {
133
- const loadingScreen = document.getElementById('loading-screen');
134
- if (loadingScreen) {
135
- loadingScreen.classList.add('hidden');
136
- setTimeout(() => {
137
- loadingScreen.style.display = 'none';
138
- }, 300);
139
- }
140
- }, 1000);
141
- },
142
-
143
- // Show specific tab
144
- showTab(tabName) {
145
- // Hide all tab contents
146
- document.querySelectorAll('.tab-content').forEach(tab => {
147
- tab.classList.remove('active');
148
- });
149
-
150
- // Remove active class from all nav tabs
151
- document.querySelectorAll('.nav-tab').forEach(tab => {
152
- tab.classList.remove('active');
153
- tab.setAttribute('aria-selected', 'false');
154
- });
155
-
156
- // Show selected tab content
157
- const targetTab = document.getElementById(`${tabName}-panel`);
158
- if (targetTab) {
159
- targetTab.classList.add('active');
160
- }
161
-
162
- // Add active class to selected nav tab
163
- const activeNavTab = document.getElementById(`${tabName}-tab`);
164
- if (activeNavTab) {
165
- activeNavTab.classList.add('active');
166
- activeNavTab.setAttribute('aria-selected', 'true');
167
- }
168
-
169
- this.activeTab = tabName;
170
-
171
- // Load models if models tab is selected
172
- if (tabName === 'models') {
173
- this.loadModels();
174
- }
175
- },
176
-
177
- // Load models for the models tab
178
- async loadModels() {
179
- if (typeof loadModels === 'function') {
180
- await loadModels();
181
- }
182
- },
183
-
184
- // Search models
185
- searchModels(query) {
186
- if (typeof WebScoutUI !== 'undefined' && WebScoutUI.searchModels) {
187
- WebScoutUI.searchModels(query);
188
- }
189
- },
190
-
191
- // Filter models
192
- filterModels(filter) {
193
- if (typeof WebScoutUI !== 'undefined' && WebScoutUI.filterModels) {
194
- WebScoutUI.filterModels(filter);
195
- }
196
- },
197
-
198
- // Toggle endpoint expansion
199
- toggleEndpoint(header) {
200
- console.log('toggleEndpoint called with:', header);
201
- const body = header.nextElementSibling;
202
- const icon = header.querySelector('.expand-icon');
203
-
204
- console.log('Body element:', body);
205
- console.log('Icon element:', icon);
206
-
207
- if (body && icon) {
208
- const isExpanded = header.classList.contains('expanded');
209
- console.log('Is expanded:', isExpanded);
210
-
211
- if (isExpanded) {
212
- header.classList.remove('expanded');
213
- body.classList.remove('expanded');
214
- body.style.display = 'none';
215
- console.log('Collapsed endpoint');
216
- } else {
217
- header.classList.add('expanded');
218
- body.classList.add('expanded');
219
- body.style.display = 'block';
220
- console.log('Expanded endpoint');
221
- }
222
-
223
- // Add animation class for smooth transition
224
- body.classList.add('animate-slideDown');
225
- setTimeout(() => {
226
- body.classList.remove('animate-slideDown');
227
- }, 300);
228
- } else {
229
- console.log('Missing body or icon element');
230
- }
231
- },
232
-
233
- // Show example language
234
- showExampleLanguage(language) {
235
- // Hide all language examples
236
- document.querySelectorAll('.language-examples').forEach(example => {
237
- example.classList.remove('active');
238
- });
239
-
240
- // Remove active class from all example tabs
241
- document.querySelectorAll('.example-tab').forEach(tab => {
242
- tab.classList.remove('active');
243
- });
244
-
245
- // Show selected language examples
246
- const targetExample = document.getElementById(`${language}-examples`);
247
- if (targetExample) {
248
- targetExample.classList.add('active');
249
- }
250
-
251
- // Add active class to selected example tab
252
- const activeTab = document.querySelector(`[data-language="${language}"]`);
253
- if (activeTab) {
254
- activeTab.classList.add('active');
255
- }
256
-
257
- // Re-highlight syntax
258
- if (typeof hljs !== 'undefined') {
259
- setTimeout(() => {
260
- hljs.highlightAll();
261
- }, 100);
262
- }
263
- },
264
-
265
- // Toggle models view (grid/list)
266
- toggleModelsView(view) {
267
- const container = document.getElementById('models-container');
268
- const buttons = document.querySelectorAll('.view-btn');
269
-
270
- if (container) {
271
- container.className = view === 'list' ? 'models-list' : 'models-grid';
272
- }
273
-
274
- buttons.forEach(btn => {
275
- btn.classList.toggle('active', btn.dataset.view === view);
276
- });
277
- },
278
-
279
- // Copy code to clipboard
280
- copyCode(elementId) {
281
- const element = document.getElementById(elementId);
282
- if (element) {
283
- const text = element.textContent || element.innerText;
284
- navigator.clipboard.writeText(text).then(() => {
285
- this.showToast('Code copied to clipboard!', 'success');
286
- }).catch(() => {
287
- this.showToast('Failed to copy code', 'error');
288
- });
289
- }
290
- },
291
-
292
- // Update status indicator
293
- async updateStatusIndicator() {
294
- try {
295
- const response = await fetch(`${this.config.baseUrl}/health`);
296
- const indicator = document.getElementById('status-indicator');
297
- if (indicator) {
298
- indicator.textContent = response.ok ? '🟢' : '🔴';
299
- }
300
- } catch {
301
- const indicator = document.getElementById('status-indicator');
302
- if (indicator) {
303
- indicator.textContent = '🔴';
304
- }
305
- }
306
- },
307
-
308
- // Initialize scroll indicator
309
- initScrollIndicator() {
310
- const scrollProgress = document.getElementById('scroll-progress');
311
- if (!scrollProgress) return;
312
-
313
- const updateScrollProgress = () => {
314
- const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
315
- const scrollHeight = document.documentElement.scrollHeight - window.innerHeight;
316
- const progress = (scrollTop / scrollHeight) * 100;
317
- scrollProgress.style.width = `${Math.min(progress, 100)}%`;
318
- };
319
-
320
- window.addEventListener('scroll', updateScrollProgress);
321
- updateScrollProgress(); // Initial call
322
- },
323
-
324
- // Toggle advanced parameters
325
- toggleAdvanced(section) {
326
- const advancedParams = document.getElementById(`${section}-advanced`);
327
- const toggleBtn = document.querySelector(`[onclick="toggleAdvanced('${section}')"]`);
328
-
329
- if (advancedParams && toggleBtn) {
330
- const isVisible = advancedParams.classList.contains('show');
331
-
332
- if (isVisible) {
333
- advancedParams.classList.remove('show');
334
- toggleBtn.classList.remove('expanded');
335
- } else {
336
- advancedParams.classList.add('show');
337
- toggleBtn.classList.add('expanded');
338
- }
339
- }
340
- },
341
-
342
- // Test chat completion endpoint with simplified parameters
343
- async testChatCompletion(event) {
344
- event.preventDefault();
345
-
346
- const startTime = Date.now();
347
-
348
- // Get form values
349
- const model = document.getElementById('chat-model').value;
350
- const messages = document.getElementById('chat-messages').value;
351
- const maxTokens = parseInt(document.getElementById('chat-max-tokens').value) || null;
352
- const temperature = parseFloat(document.getElementById('chat-temperature').value) || null;
353
- const topP = parseFloat(document.getElementById('chat-top-p').value) || null;
354
- const timeout = parseInt(document.getElementById('chat-timeout').value) || null;
355
- const stream = document.getElementById('chat-stream').checked;
356
-
357
- if (!messages.trim()) {
358
- this.showToast('Please enter messages', 'error');
359
- return;
360
- }
361
-
362
- let parsedMessages;
363
- try {
364
- parsedMessages = JSON.parse(messages);
365
- } catch (error) {
366
- this.showToast('Invalid JSON format for messages', 'error');
367
- return;
368
- }
369
-
370
- // Build request data with simplified parameters
371
- const requestData = {
372
- model: model,
373
- messages: parsedMessages,
374
- stream: stream
375
- };
376
-
377
- // Add optional parameters only if they have valid values
378
- if (maxTokens && maxTokens > 0) {
379
- requestData.max_tokens = maxTokens;
380
- }
381
-
382
- if (temperature !== null && temperature >= 0 && temperature <= 2) {
383
- requestData.temperature = temperature;
384
- }
385
-
386
- if (topP !== null && topP >= 0 && topP <= 1) {
387
- requestData.top_p = topP;
388
- }
389
-
390
- if (timeout && timeout > 0) {
391
- requestData.timeout = timeout;
392
- }
393
-
394
- const responseDiv = document.getElementById('chat-response');
395
- responseDiv.innerHTML = `
396
- <div class="loading modern-loading">
397
- <div class="loading-spinner"></div>
398
- <span>Sending request...</span>
399
- </div>
400
- `;
401
-
402
- try {
403
- const response = await fetch('/v1/chat/completions', {
404
- method: 'POST',
405
- headers: {
406
- 'Content-Type': 'application/json',
407
- 'Authorization': 'Bearer your-api-key'
408
- },
409
- body: JSON.stringify(requestData)
410
- });
411
-
412
- const responseTime = Date.now() - startTime;
413
-
414
- // Handle streaming response
415
- if (stream && response.body) {
416
- responseDiv.innerHTML = `
417
- <div class="response-header modern-response-header">
418
- <div class="response-status">
419
- <span class="status-badge status-${response.status}">${response.status} ${response.statusText}</span>
420
- <span class="response-time">Streaming...</span>
421
- </div>
422
- <button class="copy-btn" onclick="copyStreamContent()">
423
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
424
- <rect x="9" y="9" width="13" height="13" rx="2" ry="2"/>
425
- <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
426
- </svg>
427
- Copy
428
- </button>
429
- </div>
430
- <div class="response-body modern-response-body streaming">
431
- <div class="stream-content" id="stream-content"></div>
432
- <div class="stream-raw" id="stream-raw" style="display: none;"></div>
433
- <div class="stream-controls">
434
- <button class="stream-toggle" onclick="toggleStreamView()">Show Raw</button>
435
- </div>
436
- </div>
437
- `;
438
-
439
- await this.handleStreamingResponse(response, startTime);
440
- } else {
441
- // Handle regular response
442
- const result = await response.json();
443
-
444
- responseDiv.innerHTML = `
445
- <div class="response-header modern-response-header">
446
- <div class="response-status">
447
- <span class="status-badge status-${response.status}">${response.status} ${response.statusText}</span>
448
- <span class="response-time">${responseTime}ms</span>
449
- </div>
450
- <button class="copy-btn" onclick="copyToClipboard('${JSON.stringify(result).replace(/'/g, "\\'")}')">
451
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
452
- <rect x="9" y="9" width="13" height="13" rx="2" ry="2"/>
453
- <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
454
- </svg>
455
- Copy
456
- </button>
457
- </div>
458
- <pre class="response-body modern-response-body"><code class="language-json">${JSON.stringify(result, null, 2)}</code></pre>
459
- `;
460
- }
461
-
462
- this.showToast('Request completed successfully', 'success');
463
- } catch (error) {
464
- const responseTime = Date.now() - startTime;
465
- responseDiv.innerHTML = `
466
- <div class="response-header modern-response-header error">
467
- <div class="response-status">
468
- <span class="status-badge status-error">Error</span>
469
- <span class="response-time">${responseTime}ms</span>
470
- </div>
471
- </div>
472
- <div class="response-body modern-response-body error">
473
- <div class="error-content">
474
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="error-icon">
475
- <circle cx="12" cy="12" r="10"/>
476
- <line x1="15" y1="9" x2="9" y2="15"/>
477
- <line x1="9" y1="9" x2="15" y2="15"/>
478
- </svg>
479
- <div>
480
- <strong>Request Failed</strong>
481
- <p>${error.message}</p>
482
- </div>
483
- </div>
484
- </div>
485
- `;
486
- this.showToast('Request failed', 'error');
487
- }
488
- },
489
-
490
- // Handle streaming response
491
- async handleStreamingResponse(response, startTime) {
492
- const reader = response.body.getReader();
493
- const decoder = new TextDecoder();
494
- const streamContent = document.getElementById('stream-content');
495
- const streamRaw = document.getElementById('stream-raw');
496
-
497
- let fullContent = '';
498
- let rawData = '';
499
- let buffer = '';
500
-
501
- try {
502
- while (true) {
503
- const { done, value } = await reader.read();
504
-
505
- if (done) {
506
- const finalTime = Date.now() - startTime;
507
- document.querySelector('.response-time').textContent = `${finalTime}ms`;
508
- break;
509
- }
510
-
511
- // Decode the chunk
512
- const chunk = decoder.decode(value, { stream: true });
513
- buffer += chunk;
514
- rawData += chunk;
515
-
516
- // Process complete lines
517
- const lines = buffer.split('\n');
518
- buffer = lines.pop() || ''; // Keep incomplete line in buffer
519
-
520
- for (const line of lines) {
521
- if (line.trim() === '') continue;
522
-
523
- if (line.startsWith('data: ')) {
524
- const data = line.slice(6); // Remove 'data: ' prefix
525
-
526
- if (data === '[DONE]') {
527
- streamContent.innerHTML += '<div class="stream-done">✅ Stream completed</div>';
528
- continue;
529
- }
530
-
531
- try {
532
- const parsed = JSON.parse(data);
533
-
534
- // Extract content from delta
535
- if (parsed.choices && parsed.choices[0] && parsed.choices[0].delta && parsed.choices[0].delta.content) {
536
- const content = parsed.choices[0].delta.content;
537
- fullContent += content;
538
-
539
- // Update the displayed content
540
- streamContent.innerHTML = `
541
- <div class="stream-message">
542
- <strong>Generated Response:</strong>
543
- <div class="stream-text">${fullContent}</div>
544
- </div>
545
- `;
546
- }
547
-
548
- // Add to raw display
549
- streamRaw.innerHTML += `<div class="stream-chunk">data: ${JSON.stringify(parsed, null, 2)}</div>`;
550
-
551
- } catch (e) {
552
- // Handle non-JSON data lines
553
- streamRaw.innerHTML += `<div class="stream-chunk">data: ${data}</div>`;
554
- }
555
- }
556
- }
557
-
558
- // Auto-scroll to bottom
559
- streamContent.scrollTop = streamContent.scrollHeight;
560
- streamRaw.scrollTop = streamRaw.scrollHeight;
561
- }
562
- } catch (error) {
563
- streamContent.innerHTML += `<div class="stream-error">❌ Stream error: ${error.message}</div>`;
564
- }
565
-
566
- // Store full content for copying
567
- window.streamFullContent = fullContent;
568
- window.streamRawData = rawData;
569
- },
570
-
571
- // Test image generation endpoint
572
- async testImageGeneration(event) {
573
- event.preventDefault();
574
-
575
- const startTime = Date.now();
576
-
577
- // Get form values
578
- const model = document.getElementById('image-model').value;
579
- const prompt = document.getElementById('image-prompt').value;
580
- const size = document.getElementById('image-size').value;
581
- const count = parseInt(document.getElementById('image-count').value);
582
-
583
- if (!prompt.trim()) {
584
- this.showToast('Please enter a prompt', 'error');
585
- return;
586
- }
587
-
588
- // Build request data
589
- const requestData = {
590
- model: model,
591
- prompt: prompt,
592
- size: size,
593
- n: count
594
- };
595
-
596
- const responseDiv = document.getElementById('image-response');
597
- responseDiv.innerHTML = `
598
- <div class="loading modern-loading">
599
- <div class="loading-spinner"></div>
600
- <span>Generating image...</span>
601
- </div>
602
- `;
603
-
604
- try {
605
- const response = await fetch('/v1/images/generations', {
606
- method: 'POST',
607
- headers: {
608
- 'Content-Type': 'application/json',
609
- 'Authorization': 'Bearer your-api-key'
610
- },
611
- body: JSON.stringify(requestData)
612
- });
613
-
614
- const result = await response.json();
615
- const responseTime = Date.now() - startTime;
616
-
617
- responseDiv.innerHTML = `
618
- <div class="response-header modern-response-header">
619
- <div class="response-status">
620
- <span class="status-badge status-${response.status}">${response.status} ${response.statusText}</span>
621
- <span class="response-time">${responseTime}ms</span>
622
- </div>
623
- <button class="copy-btn" onclick="copyToClipboard('${JSON.stringify(result).replace(/'/g, "\\'")}')">
624
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
625
- <rect x="9" y="9" width="13" height="13" rx="2" ry="2"/>
626
- <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
627
- </svg>
628
- Copy
629
- </button>
630
- </div>
631
- <pre class="response-body modern-response-body"><code class="language-json">${JSON.stringify(result, null, 2)}</code></pre>
632
- `;
633
-
634
- this.showToast('Image generation completed successfully', 'success');
635
- } catch (error) {
636
- const responseTime = Date.now() - startTime;
637
- responseDiv.innerHTML = `
638
- <div class="response-header modern-response-header error">
639
- <div class="response-status">
640
- <span class="status-badge status-error">Error</span>
641
- <span class="response-time">${responseTime}ms</span>
642
- </div>
643
- </div>
644
- <div class="response-body modern-response-body error">
645
- <div class="error-content">
646
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="error-icon">
647
- <circle cx="12" cy="12" r="10"/>
648
- <line x1="15" y1="9" x2="9" y2="15"/>
649
- <line x1="9" y1="9" x2="15" y2="15"/>
650
- </svg>
651
- <div>
652
- <strong>Image Generation Failed</strong>
653
- <p>${error.message}</p>
654
- </div>
655
- </div>
656
- </div>
657
- `;
658
- this.showToast('Image generation failed', 'error');
659
- }
660
- },
661
-
662
- // Show toast notification
663
- showToast(message, type = 'info', duration = 3000) {
664
- const container = document.getElementById('toast-container');
665
- if (!container) return;
666
-
667
- const toast = document.createElement('div');
668
- toast.className = `toast ${type} animate-slideInRight`;
669
-
670
- const icons = {
671
- success: '✅',
672
- error: '❌',
673
- warning: '⚠️',
674
- info: 'ℹ️'
675
- };
676
-
677
- toast.innerHTML = `
678
- <span class="toast-icon">${icons[type] || icons.info}</span>
679
- <div class="toast-content">
680
- <div class="toast-message">${message}</div>
681
- </div>
682
- <button class="toast-close" onclick="this.parentElement.remove()">×</button>
683
- `;
684
-
685
- container.appendChild(toast);
686
-
687
- // Auto remove after duration
688
- setTimeout(() => {
689
- if (toast.parentElement) {
690
- toast.classList.add('animate-slideOutRight');
691
- setTimeout(() => {
692
- if (toast.parentElement) {
693
- toast.remove();
694
- }
695
- }, 300);
696
- }
697
- }, duration);
698
- }
699
- };
700
-
701
- // Global functions for backward compatibility
702
- function showTab(tabName) {
703
- WebScoutApp.showTab(tabName);
704
- }
705
-
706
- function toggleEndpoint(header) {
707
- WebScoutApp.toggleEndpoint(header);
708
- }
709
-
710
- function copyCode(elementId) {
711
- WebScoutApp.copyCode(elementId);
712
- }
713
-
714
- function copyToClipboard(text) {
715
- navigator.clipboard.writeText(text).then(() => {
716
- WebScoutApp.showToast('Copied to clipboard!', 'success');
717
- }).catch(() => {
718
- WebScoutApp.showToast('Failed to copy', 'error');
719
- });
720
- }
721
-
722
- // Global function for advanced toggle (called from HTML)
723
- function toggleAdvanced(section) {
724
- WebScoutApp.toggleAdvanced(section);
725
- }
726
-
727
- // Global function for testing chat completion (called from HTML)
728
- function testChatCompletion(event) {
729
- WebScoutApp.testChatCompletion(event);
730
- }
731
-
732
- // Global function for testing image generation (called from HTML)
733
- function testImageGeneration(event) {
734
- WebScoutApp.testImageGeneration(event);
735
- }
736
-
737
- // Global function to toggle stream view
738
- function toggleStreamView() {
739
- const streamContent = document.getElementById('stream-content');
740
- const streamRaw = document.getElementById('stream-raw');
741
- const toggleBtn = document.querySelector('.stream-toggle');
742
-
743
- if (streamRaw.style.display === 'none') {
744
- streamContent.style.display = 'none';
745
- streamRaw.style.display = 'block';
746
- toggleBtn.textContent = 'Show Formatted';
747
- } else {
748
- streamContent.style.display = 'block';
749
- streamRaw.style.display = 'none';
750
- toggleBtn.textContent = 'Show Raw';
751
- }
752
- }
753
-
754
- // Global function to copy stream content
755
- function copyStreamContent() {
756
- const content = window.streamFullContent || '';
757
- if (content) {
758
- navigator.clipboard.writeText(content).then(() => {
759
- WebScoutApp.showToast('Stream content copied to clipboard!', 'success');
760
- }).catch(() => {
761
- WebScoutApp.showToast('Failed to copy stream content', 'error');
762
- });
763
- } else {
764
- WebScoutApp.showToast('No stream content to copy', 'warning');
765
- }
766
- }
767
-
768
- // Utility functions
769
- function formatJSON(obj) {
770
- return JSON.stringify(obj, null, 2);
771
- }
772
-
773
- function formatTimestamp(timestamp) {
774
- return new Date(timestamp).toLocaleString();
775
- }
776
-
777
- function generateId() {
778
- return Math.random().toString(36).substr(2, 9);
779
- }
780
-
781
- // Initialize the application when DOM is loaded
782
- document.addEventListener('DOMContentLoaded', function() {
783
- WebScoutApp.init();
784
- });