udd542 commited on
Commit
01dde67
·
verified ·
1 Parent(s): 409b6ed

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +996 -568
index.html CHANGED
@@ -1,597 +1,1025 @@
1
  <!DOCTYPE html>
2
  <html lang="en">
3
  <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Ethical AI Chat Assistant</title>
7
- <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
8
- <style>
9
- * {
10
- margin: 0;
11
- padding: 0;
12
- box-sizing: border-box;
13
- }
14
-
15
- body {
16
- font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
17
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
18
- min-height: 100vh;
19
- display: flex;
20
- align-items: center;
21
- justify-content: center;
22
- padding: 20px;
23
- }
24
-
25
- .app-container {
26
- width: 100%;
27
- max-width: 800px;
28
- background: rgba(255, 255, 255, 0.95);
29
- backdrop-filter: blur(10px);
30
- border-radius: 20px;
31
- box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
32
- overflow: hidden;
33
- display: flex;
34
- flex-direction: column;
35
- height: 90vh;
36
- max-height: 700px;
37
- }
38
-
39
- .header {
40
- background: linear-gradient(135deg, #667eea, #764ba2);
41
- color: white;
42
- padding: 20px;
43
- text-align: center;
44
- position: relative;
45
- }
46
-
47
- .header h1 {
48
- font-size: 1.8rem;
49
- margin-bottom: 5px;
50
- }
51
-
52
- .header p {
53
- opacity: 0.9;
54
- font-size: 0.9rem;
55
- }
56
-
57
- .api-config {
58
- background: #f8f9fa;
59
- padding: 15px;
60
- border-bottom: 1px solid #e9ecef;
61
- }
62
-
63
- .config-group {
64
- display: flex;
65
- gap: 10px;
66
- align-items: center;
67
- flex-wrap: wrap;
68
- }
69
-
70
- .config-group input {
71
- flex: 1;
72
- min-width: 200px;
73
- padding: 8px 12px;
74
- border: 1px solid #ddd;
75
- border-radius: 8px;
76
- font-size: 14px;
77
- }
78
-
79
- .config-group button {
80
- padding: 8px 16px;
81
- background: #667eea;
82
- color: white;
83
- border: none;
84
- border-radius: 8px;
85
- cursor: pointer;
86
- transition: background 0.3s;
87
- }
88
-
89
- .config-group button:hover {
90
- background: #5a67d8;
91
- }
92
-
93
- .status-indicator {
94
- font-size: 12px;
95
- color: #666;
96
- margin-top: 5px;
97
- }
98
-
99
- .chat-container {
100
- flex: 1;
101
- display: flex;
102
- flex-direction: column;
103
- overflow: hidden;
104
- }
105
-
106
- .messages {
107
- flex: 1;
108
- overflow-y: auto;
109
- padding: 20px;
110
- background: #fff;
111
- }
112
-
113
- .message {
114
- margin-bottom: 20px;
115
- display: flex;
116
- align-items: flex-start;
117
- gap: 12px;
118
- }
119
-
120
- .message.user {
121
- flex-direction: row-reverse;
122
- }
123
-
124
- .message-avatar {
125
- width: 40px;
126
- height: 40px;
127
- border-radius: 50%;
128
- display: flex;
129
- align-items: center;
130
- justify-content: center;
131
- color: white;
132
- font-weight: bold;
133
- flex-shrink: 0;
134
- }
135
-
136
- .message.user .message-avatar {
137
- background: linear-gradient(135deg, #667eea, #764ba2);
138
- }
139
-
140
- .message.assistant .message-avatar {
141
- background: linear-gradient(135deg, #ff6b6b, #ee5a24);
142
- }
143
-
144
- .message-content {
145
- max-width: 70%;
146
- padding: 15px 20px;
147
- border-radius: 18px;
148
- line-height: 1.5;
149
- word-wrap: break-word;
150
- }
151
-
152
- .message.user .message-content {
153
- background: linear-gradient(135deg, #667eea, #764ba2);
154
- color: white;
155
- }
156
-
157
- .message.assistant .message-content {
158
- background: #f1f3f4;
159
- color: #333;
160
- }
161
-
162
- .input-container {
163
- padding: 20px;
164
- background: #f8f9fa;
165
- border-top: 1px solid #e9ecef;
166
- }
167
-
168
- .input-group {
169
- display: flex;
170
- gap: 10px;
171
- align-items: flex-end;
172
- }
173
-
174
- .input-group textarea {
175
- flex: 1;
176
- min-height: 50px;
177
- max-height: 120px;
178
- padding: 12px 15px;
179
- border: 1px solid #ddd;
180
- border-radius: 25px;
181
- resize: vertical;
182
- font-family: inherit;
183
- font-size: 14px;
184
- outline: none;
185
- transition: border-color 0.3s;
186
- }
187
-
188
- .input-group textarea:focus {
189
- border-color: #667eea;
190
- }
191
-
192
- .send-button {
193
- width: 50px;
194
- height: 50px;
195
- border-radius: 50%;
196
- background: linear-gradient(135deg, #667eea, #764ba2);
197
- color: white;
198
- border: none;
199
- cursor: pointer;
200
- display: flex;
201
- align-items: center;
202
- justify-content: center;
203
- transition: transform 0.3s, box-shadow 0.3s;
204
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
 
206
- .send-button:hover {
207
- transform: translateY(-2px);
208
- box-shadow: 0 10px 20px rgba(102, 126, 234, 0.3);
209
- }
 
 
 
 
 
 
 
210
 
211
- .send-button:disabled {
212
- opacity: 0.6;
213
- cursor: not-allowed;
214
- transform: none;
215
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
216
 
217
- .safety-notice {
218
- background: #e3f2fd;
219
- color: #1565c0;
220
- padding: 10px 15px;
221
- border-radius: 8px;
222
- font-size: 12px;
223
- margin-bottom: 10px;
224
- border-left: 4px solid #2196f3;
225
- }
226
 
227
- .typing-indicator {
228
- padding: 10px 20px;
229
- background: #f1f3f4;
230
- border-radius: 18px;
231
- display: inline-flex;
232
- gap: 5px;
233
- margin-bottom: 20px;
234
- }
 
 
 
 
 
 
 
 
 
 
235
 
236
- .typing-dot {
237
- width: 8px;
238
- height: 8px;
239
- border-radius: 50%;
240
- background: #999;
241
- animation: typing 1.4s infinite;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
242
  }
243
 
244
- .typing-dot:nth-child(2) {
245
- animation-delay: 0.2s;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
246
  }
247
-
248
- .typing-dot:nth-child(3) {
249
- animation-delay: 0.4s;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
250
  }
251
 
252
- @keyframes typing {
253
- 0%, 60%, 100% {
254
- transform: translateY(0);
255
- opacity: 0.3;
256
- }
257
- 30% {
258
- transform: translateY(-10px);
259
- opacity: 1;
260
- }
261
- }
262
 
263
- .loading {
264
- display: flex;
265
- align-items: center;
266
- gap: 10px;
267
- color: #666;
268
- font-size: 14px;
269
  }
270
 
271
- .loading::after {
272
- content: '';
273
- width: 16px;
274
- height: 16px;
275
- border: 2px solid #667eea;
276
- border-top: 2px solid transparent;
277
- border-radius: 50%;
278
- animation: spin 1s linear infinite;
279
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
280
 
281
- @keyframes spin {
282
- to {
283
- transform: rotate(360deg);
284
- }
285
  }
286
 
287
- @media (max-width: 768px) {
288
- .app-container {
289
- height: 95vh;
290
- max-height: none;
291
- border-radius: 0;
292
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
293
 
294
- .config-group {
295
- flex-direction: column;
296
- align-items: stretch;
297
- }
298
 
299
- .config-group input {
300
- min-width: unset;
301
- }
302
 
303
- .message-content {
304
- max-width: 85%;
305
- }
306
  }
307
-
308
- .brand {
309
- color: #333;
310
- text-decoration: none;
311
- font-weight: bold;
312
- display: inline-flex;
313
- align-items: center;
314
- gap: 5px;
315
- }
316
-
317
- .brand:hover {
318
- color: #667eea;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
319
  }
320
- </style>
321
- </head>
322
- <body>
323
- <div class="app-container">
324
- <div class="header">
325
- <h1><i class="fas fa-robot"></i> Ethical AI Chat Assistant</h1>
326
- <p>Responsible AI powered conversations with built-in safety measures</p>
327
- <div style="margin-top: 10px; font-size: 12px;">
328
- Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" class="brand" target="_blank">anycoder</a>
 
 
 
 
 
 
 
 
 
 
 
 
 
329
  </div>
330
- </div>
331
-
332
- <div class="api-config">
333
- <div class="config-group">
334
- <input type="text" id="apiKey" placeholder="Enter your OpenAI API key (sk-...)" />
335
- <input type="text" id="modelName" placeholder="Model (default: gpt-3.5-turbo)" value="gpt-3.5-turbo" />
336
- <button onclick="saveConfig()"><i class="fas fa-save"></i> Save</button>
337
- </div>
338
- <div class="status-indicator" id="status">
339
- <i class="fas fa-info-circle"></i> Enter your API key to start chatting
340
- </div>
341
- </div>
342
-
343
- <div class="safety-notice">
344
- <i class="fas fa-shield-alt"></i> This chat assistant includes safety guidelines to promote responsible AI use and prevent harmful content generation.
345
- </div>
346
-
347
- <div class="chat-container">
348
- <div class="messages" id="messages">
349
- <div class="message assistant">
350
- <div class="message-avatar">AI</div>
351
- <div class="message-content">
352
- Hello! I'm an ethical AI assistant designed to help with legitimate tasks while maintaining safety guidelines.
353
- Please configure your API key above to begin our conversation.
354
- </div>
355
- </div>
356
- </div>
357
- </div>
358
-
359
- <div class="input-container">
360
- <div class="input-group">
361
- <textarea id="messageInput" placeholder="Type your message here... (remember to follow safety guidelines)"
362
- onkeydown="handleKeyDown(event)"></textarea>
363
- <button class="send-button" id="sendButton" onclick="sendMessage()">
364
- <i class="fas fa-paper-plane"></i>
365
- </button>
366
- </div>
367
- </div>
368
- </div>
369
-
370
- <script>
371
- class EthicalChatApp {
372
- constructor() {
373
- this.apiKey = '';
374
- this.model = 'gpt-3.5-turbo';
375
- this.isLoading = false;
376
- this.messages = [];
377
-
378
- this.loadConfig();
379
- this.initializeEventListeners();
380
- this.updateUI();
381
- }
382
-
383
- loadConfig() {
384
- this.apiKey = localStorage.getItem('openai_api_key') || '';
385
- this.model = localStorage.getItem('openai_model') || 'gpt-3.5-turbo';
386
-
387
- document.getElementById('apiKey').value = this.apiKey;
388
- document.getElementById('modelName').value = this.model;
389
-
390
- this.updateStatus();
391
- }
392
-
393
- saveConfig() {
394
- this.apiKey = document.getElementById('apiKey').value.trim();
395
- this.model = document.getElementById('modelName').value.trim() || 'gpt-3.5-turbo';
396
-
397
- if (this.apiKey) {
398
- localStorage.setItem('openai_api_key', this.apiKey);
399
- localStorage.setItem('openai_model', this.model);
400
- this.updateStatus();
401
- this.showMessage('Configuration saved successfully!', 'assistant');
402
- } else {
403
- this.updateStatus('Please enter a valid API key', 'error');
404
- }
405
- }
406
-
407
- updateStatus(message = '', type = 'info') {
408
- const statusEl = document.getElementById('status');
409
- const icon = type === 'error' ? 'exclamation-triangle' :
410
- type === 'success' ? 'check-circle' : 'info-circle';
411
- const color = type === 'error' ? '#e74c3c' :
412
- type === 'success' ? '#27ae60' : '#666';
413
-
414
- statusEl.innerHTML = `<i class="fas fa-${icon}" style="color: ${color}"></i> ${message || (this.apiKey ? 'Ready to chat!' : 'Enter your API key to start chatting')}`;
415
- }
416
-
417
- async sendMessage() {
418
- const input = document.getElementById('messageInput');
419
- const message = input.value.trim();
420
-
421
- if (!message || this.isLoading) return;
422
- if (!this.apiKey) {
423
- this.updateStatus('Please configure your API key first', 'error');
424
- return;
425
- }
426
-
427
- input.value = '';
428
- this.isLoading = true;
429
- this.updateUI();
430
-
431
- // Add user message
432
- this.addMessage(message, 'user');
433
-
434
- // Show typing indicator
435
- this.showTypingIndicator();
436
-
437
- try {
438
- const response = await this.callOpenAI(message);
439
- this.removeTypingIndicator();
440
- this.addMessage(response, 'assistant');
441
- } catch (error) {
442
- this.removeTypingIndicator();
443
- this.addMessage(`Error: ${error.message}`, 'assistant');
444
- console.error('API Error:', error);
445
- } finally {
446
- this.isLoading = false;
447
- this.updateUI();
448
- }
449
- }
450
-
451
- async callOpenAI(message) {
452
- const messages = this.buildMessageHistory(message);
453
-
454
- const response = await fetch('https://api.openai.com/v1/chat/completions', {
455
- method: 'POST',
456
- headers: {
457
- 'Content-Type': 'application/json',
458
- 'Authorization': `Bearer ${this.apiKey}`
459
- },
460
- body: JSON.stringify({
461
- model: this.model,
462
- messages: messages,
463
- max_tokens: 1000,
464
- temperature: 0.7,
465
- presence_penalty: 0.1,
466
- frequency_penalty: 0.1
467
- })
468
- });
469
-
470
- if (!response.ok) {
471
- const error = await response.json();
472
- throw new Error(error.error?.message || 'API request failed');
473
- }
474
-
475
- const data = await response.json();
476
- return data.choices[0].message.content;
477
- }
478
-
479
- buildMessageHistory(currentMessage) {
480
- const systemPrompt = `You are an ethical AI assistant designed to be helpful, harmless, and honest.
481
-
482
- IMPORTANT SAFETY GUIDELINES:
483
- - Do not provide instructions for creating harmful substances, weapons, or illegal activities
484
- - Do not generate content that promotes violence, discrimination, or hatred
485
- - Do not provide detailed information for dangerous or illegal purposes
486
- - Do not impersonate real people or create fake credentials
487
- - Encourage positive, constructive conversations
488
- - Be honest about your limitations and uncertainties
489
- - Provide helpful responses while maintaining ethical standards
490
-
491
- Always prioritize safety and responsible AI use in your responses.`;
492
-
493
- const messages = [
494
- { role: 'system', content: systemPrompt },
495
- ...this.messages.slice(-10), // Keep last 10 messages for context
496
- { role: 'user', content: currentMessage }
497
- ];
498
-
499
- this.messages.push({ role: 'user', content: currentMessage });
500
- return messages;
501
- }
502
-
503
- addMessage(content, sender) {
504
- const messagesEl = document.getElementById('messages');
505
- const messageEl = document.createElement('div');
506
- messageEl.className = `message ${sender}`;
507
-
508
- const avatar = sender === 'user' ? 'U' : 'AI';
509
- const avatarClass = sender === 'user' ? 'user' : 'assistant';
510
-
511
- messageEl.innerHTML = `
512
- <div class="message-avatar">${avatar}</div>
513
- <div class="message-content">${this.formatMessage(content)}</div>
514
- `;
515
-
516
- messagesEl.appendChild(messageEl);
517
- messagesEl.scrollTop = messagesEl.scrollHeight;
518
-
519
- if (sender === 'assistant') {
520
- this.messages.push({ role: 'assistant', content: content });
521
- }
522
- }
523
-
524
- formatMessage(content) {
525
- return content
526
- .replace(/\n/g, '<br>')
527
- .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
528
- .replace(/\*(.*?)\*/g, '<em>$1</em>')
529
- .replace(/`(.*?)`/g, '<code style="background: #f4f4f4; padding: 2px 4px; border-radius: 3px;">$1</code>');
530
- }
531
-
532
- showTypingIndicator() {
533
- const messagesEl = document.getElementById('messages');
534
- const typingEl = document.createElement('div');
535
- typingEl.className = 'message assistant';
536
- typingEl.id = 'typing-indicator';
537
- typingEl.innerHTML = `
538
- <div class="message-avatar">AI</div>
539
- <div class="message-content">
540
- <div class="typing-indicator">
541
- <div class="typing-dot"></div>
542
- <div class="typing-dot"></div>
543
- <div class="typing-dot"></div>
544
- </div>
545
- </div>
546
- `;
547
- messagesEl.appendChild(typingEl);
548
- messagesEl.scrollTop = messagesEl.scrollHeight;
549
- }
550
-
551
- removeTypingIndicator() {
552
- const typingEl = document.getElementById('typing-indicator');
553
- if (typingEl) {
554
- typingEl.remove();
555
- }
556
- }
557
-
558
- updateUI() {
559
- const sendButton = document.getElementById('sendButton');
560
- const input = document.getElementById('messageInput');
561
-
562
- sendButton.disabled = this.isLoading || !this.apiKey;
563
- input.disabled = this.isLoading;
564
- }
565
-
566
- initializeEventListeners() {
567
- document.getElementById('messageInput').addEventListener('input', () => {
568
- this.updateUI();
569
- });
570
- }
571
- }
572
-
573
- // Global functions
574
- let chatApp;
575
-
576
- function saveConfig() {
577
- chatApp.saveConfig();
578
- }
579
-
580
- function sendMessage() {
581
- chatApp.sendMessage();
582
  }
583
-
584
- function handleKeyDown(event) {
585
- if (event.key === 'Enter' && !event.shiftKey) {
586
- event.preventDefault();
587
- sendMessage();
588
- }
589
- }
590
-
591
- // Initialize the application
592
- document.addEventListener('DOMContentLoaded', () => {
593
- chatApp = new EthicalChatApp();
 
 
594
  });
595
- </script>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
596
  </body>
597
  </html>
 
1
  <!DOCTYPE html>
2
  <html lang="en">
3
  <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Multi-Provider AI Chat Assistant</title>
7
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
8
+ <style>
9
+ * {
10
+ margin: 0;
11
+ padding: 0;
12
+ box-sizing: border-box;
13
+ }
14
+
15
+ :root {
16
+ --primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
17
+ --secondary-gradient: linear-gradient(135deg, #ff6b6b, #ee5a24);
18
+ --success-color: #27ae60;
19
+ --error-color: #e74c3c;
20
+ --warning-color: #f39c12;
21
+ --bg-light: rgba(255, 255, 255, 0.95);
22
+ --shadow-light: 0 10px 30px rgba(0, 0, 0, 0.1);
23
+ --shadow-medium: 0 20px 40px rgba(0, 0, 0, 0.15);
24
+ }
25
+
26
+ body {
27
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
28
+ background: var(--primary-gradient);
29
+ min-height: 100vh;
30
+ display: flex;
31
+ align-items: center;
32
+ justify-content: center;
33
+ padding: clamp(5px, 2vw, 20px);
34
+ line-height: 1.6;
35
+ }
36
+
37
+ .app-container {
38
+ width: 100%;
39
+ max-width: 1000px;
40
+ background: var(--bg-light);
41
+ backdrop-filter: blur(15px);
42
+ border-radius: clamp(15px, 2vw, 25px);
43
+ box-shadow: var(--shadow-medium);
44
+ overflow: hidden;
45
+ display: flex;
46
+ flex-direction: column;
47
+ height: 95vh;
48
+ max-height: 800px;
49
+ min-height: 600px;
50
+ }
51
+
52
+ .header {
53
+ background: var(--primary-gradient);
54
+ color: white;
55
+ padding: clamp(15px, 3vw, 25px);
56
+ text-align: center;
57
+ position: relative;
58
+ overflow: hidden;
59
+ }
60
+
61
+ .header::before {
62
+ content: '';
63
+ position: absolute;
64
+ top: -50%;
65
+ left: -50%;
66
+ width: 200%;
67
+ height: 200%;
68
+ background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%);
69
+ animation: shimmer 3s ease-in-out infinite;
70
+ }
71
+
72
+ @keyframes shimmer {
73
+ 0%, 100% { transform: translateX(-100%) translateY(-100%) rotate(0deg); }
74
+ 50% { transform: translateX(100%) translateY(100%) rotate(180deg); }
75
+ }
76
+
77
+ .header h1 {
78
+ font-size: clamp(1.4rem, 3.5vw, 2.2rem);
79
+ margin-bottom: clamp(5px, 1vw, 10px);
80
+ position: relative;
81
+ z-index: 1;
82
+ }
83
+
84
+ .header p {
85
+ opacity: 0.9;
86
+ font-size: clamp(0.85rem, 2vw, 1rem);
87
+ position: relative;
88
+ z-index: 1;
89
+ }
90
+
91
+ .brand-link {
92
+ color: white;
93
+ text-decoration: none;
94
+ font-weight: bold;
95
+ display: inline-flex;
96
+ align-items: center;
97
+ gap: clamp(3px, 0.8vw, 8px);
98
+ font-size: clamp(0.75rem, 1.8vw, 0.9rem);
99
+ position: relative;
100
+ z-index: 1;
101
+ transition: all 0.3s ease;
102
+ }
103
+
104
+ .brand-link:hover {
105
+ color: #ffd700;
106
+ transform: translateY(-1px);
107
+ }
108
+
109
+ .api-config {
110
+ background: linear-gradient(135deg, #f8f9fa, #e9ecef);
111
+ padding: clamp(12px, 2.5vw, 18px);
112
+ border-bottom: 1px solid #dee2e6;
113
+ position: relative;
114
+ }
115
+
116
+ .provider-selector {
117
+ margin-bottom: clamp(8px, 1.5vw, 12px);
118
+ }
119
+
120
+ .provider-selector label {
121
+ display: block;
122
+ font-size: clamp(0.8rem, 1.8vw, 0.9rem);
123
+ font-weight: 600;
124
+ color: #495057;
125
+ margin-bottom: clamp(4px, 1vw, 8px);
126
+ }
127
+
128
+ .provider-selector select {
129
+ width: 100%;
130
+ padding: clamp(8px, 1.8vw, 10px) clamp(10px, 2vw, 12px);
131
+ border: 2px solid #dee2e6;
132
+ border-radius: clamp(8px, 1.5vw, 10px);
133
+ font-size: clamp(0.85rem, 1.8vw, 0.9rem);
134
+ background: white;
135
+ transition: all 0.3s ease;
136
+ }
137
+
138
+ .provider-selector select:focus {
139
+ outline: none;
140
+ border-color: #667eea;
141
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
142
+ }
143
+
144
+ .config-group {
145
+ display: grid;
146
+ grid-template-columns: 1fr 1fr auto;
147
+ gap: clamp(8px, 1.5vw, 12px);
148
+ align-items: end;
149
+ }
150
+
151
+ .config-group input {
152
+ padding: clamp(8px, 1.8vw, 10px) clamp(10px, 2vw, 12px);
153
+ border: 2px solid #dee2e6;
154
+ border-radius: clamp(8px, 1.5vw, 10px);
155
+ font-size: clamp(0.8rem, 1.8vw, 0.9rem);
156
+ transition: all 0.3s ease;
157
+ background: white;
158
+ }
159
+
160
+ .config-group input:focus {
161
+ outline: none;
162
+ border-color: #667eea;
163
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
164
+ }
165
+
166
+ .config-group input[placeholder*="API"] {
167
+ grid-column: 1 / -1;
168
+ }
169
+
170
+ .config-group button {
171
+ padding: clamp(10px, 2vw, 12px) clamp(15px, 3vw, 20px);
172
+ background: var(--primary-gradient);
173
+ color: white;
174
+ border: none;
175
+ border-radius: clamp(8px, 1.5vw, 10px);
176
+ cursor: pointer;
177
+ font-weight: 600;
178
+ transition: all 0.3s ease;
179
+ font-size: clamp(0.8rem, 1.8vw, 0.9rem);
180
+ white-space: nowrap;
181
+ }
182
+
183
+ .config-group button:hover {
184
+ transform: translateY(-2px);
185
+ box-shadow: 0 8px 20px rgba(102, 126, 234, 0.3);
186
+ }
187
+
188
+ .config-group button:active {
189
+ transform: translateY(0);
190
+ }
191
+
192
+ .status-indicator {
193
+ font-size: clamp(0.75rem, 1.6vw, 0.85rem);
194
+ color: #6c757d;
195
+ margin-top: clamp(5px, 1vw, 8px);
196
+ display: flex;
197
+ align-items: center;
198
+ gap: clamp(4px, 1vw, 8px);
199
+ }
200
+
201
+ .status-indicator.success { color: var(--success-color); }
202
+ .status-indicator.error { color: var(--error-color); }
203
+ .status-indicator.warning { color: var(--warning-color); }
204
+
205
+ .chat-container {
206
+ flex: 1;
207
+ display: flex;
208
+ flex-direction: column;
209
+ overflow: hidden;
210
+ min-height: 0;
211
+ }
212
+
213
+ .messages {
214
+ flex: 1;
215
+ overflow-y: auto;
216
+ padding: clamp(15px, 3vw, 25px);
217
+ background: linear-gradient(135deg, #ffffff, #f8f9fa);
218
+ scroll-behavior: smooth;
219
+ }
220
+
221
+ .messages::-webkit-scrollbar {
222
+ width: 6px;
223
+ }
224
+
225
+ .messages::-webkit-scrollbar-track {
226
+ background: #f1f1f1;
227
+ border-radius: 3px;
228
+ }
229
+
230
+ .messages::-webkit-scrollbar-thumb {
231
+ background: #c1c1c1;
232
+ border-radius: 3px;
233
+ }
234
+
235
+ .messages::-webkit-scrollbar-thumb:hover {
236
+ background: #a8a8a8;
237
+ }
238
+
239
+ .message {
240
+ margin-bottom: clamp(15px, 3vw, 25px);
241
+ display: flex;
242
+ align-items: flex-start;
243
+ gap: clamp(10px, 2vw, 15px);
244
+ animation: fadeInUp 0.4s ease-out;
245
+ }
246
+
247
+ @keyframes fadeInUp {
248
+ from {
249
+ opacity: 0;
250
+ transform: translateY(20px);
251
+ }
252
+ to {
253
+ opacity: 1;
254
+ transform: translateY(0);
255
+ }
256
+ }
257
+
258
+ .message.user {
259
+ flex-direction: row-reverse;
260
+ }
261
+
262
+ .message-avatar {
263
+ width: clamp(35px, 6vw, 45px);
264
+ height: clamp(35px, 6vw, 45px);
265
+ border-radius: 50%;
266
+ display: flex;
267
+ align-items: center;
268
+ justify-content: center;
269
+ color: white;
270
+ font-weight: bold;
271
+ flex-shrink: 0;
272
+ font-size: clamp(0.8rem, 2vw, 1rem);
273
+ position: relative;
274
+ overflow: hidden;
275
+ }
276
+
277
+ .message.user .message-avatar {
278
+ background: var(--primary-gradient);
279
+ }
280
+
281
+ .message.assistant .message-avatar {
282
+ background: var(--secondary-gradient);
283
+ }
284
+
285
+ .message-avatar::before {
286
+ content: '';
287
+ position: absolute;
288
+ top: 0;
289
+ left: 0;
290
+ right: 0;
291
+ bottom: 0;
292
+ background: linear-gradient(45deg, transparent, rgba(255,255,255,0.2));
293
+ animation: pulse 2s ease-in-out infinite;
294
+ }
295
+
296
+ @keyframes pulse {
297
+ 0%, 100% { opacity: 0; }
298
+ 50% { opacity: 1; }
299
+ }
300
+
301
+ .message-content {
302
+ max-width: min(80%, 600px);
303
+ padding: clamp(12px, 2.5vw, 18px) clamp(15px, 3vw, 22px);
304
+ border-radius: clamp(15px, 3vw, 20px);
305
+ line-height: 1.6;
306
+ word-wrap: break-word;
307
+ position: relative;
308
+ }
309
+
310
+ .message.user .message-content {
311
+ background: var(--primary-gradient);
312
+ color: white;
313
+ }
314
+
315
+ .message.assistant .message-content {
316
+ background: #ffffff;
317
+ color: #333;
318
+ box-shadow: var(--shadow-light);
319
+ border: 1px solid #e9ecef;
320
+ }
321
+
322
+ .message.assistant .message-content:hover {
323
+ box-shadow: var(--shadow-medium);
324
+ transform: translateY(-1px);
325
+ transition: all 0.3s ease;
326
+ }
327
+
328
+ .input-container {
329
+ padding: clamp(15px, 3vw, 25px);
330
+ background: linear-gradient(135deg, #f8f9fa, #e9ecef);
331
+ border-top: 1px solid #dee2e6;
332
+ }
333
+
334
+ .safety-notice {
335
+ background: linear-gradient(135deg, #e3f2fd, #bbdefb);
336
+ color: #1565c0;
337
+ padding: clamp(8px, 2vw, 12px) clamp(12px, 2.5vw, 16px);
338
+ border-radius: clamp(8px, 1.5vw, 12px);
339
+ font-size: clamp(0.75rem, 1.6vw, 0.85rem);
340
+ margin-bottom: clamp(10px, 2vw, 15px);
341
+ border-left: 4px solid #2196f3;
342
+ display: flex;
343
+ align-items: center;
344
+ gap: clamp(6px, 1.2vw, 10px);
345
+ }
346
+
347
+ .input-group {
348
+ display: flex;
349
+ gap: clamp(8px, 1.5vw, 12px);
350
+ align-items: flex-end;
351
+ }
352
+
353
+ .input-group textarea {
354
+ flex: 1;
355
+ min-height: clamp(45px, 8vh, 60px);
356
+ max-height: clamp(100px, 15vh, 140px);
357
+ padding: clamp(10px, 2.5vw, 15px) clamp(12px, 2.5vw, 18px);
358
+ border: 2px solid #dee2e6;
359
+ border-radius: clamp(20px, 4vw, 30px);
360
+ resize: vertical;
361
+ font-family: inherit;
362
+ font-size: clamp(0.85rem, 1.8vw, 1rem);
363
+ outline: none;
364
+ transition: all 0.3s ease;
365
+ background: white;
366
+ }
367
+
368
+ .input-group textarea:focus {
369
+ border-color: #667eea;
370
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
371
+ }
372
+
373
+ .send-button {
374
+ width: clamp(45px, 8vw, 55px);
375
+ height: clamp(45px, 8vw, 55px);
376
+ border-radius: 50%;
377
+ background: var(--primary-gradient);
378
+ color: white;
379
+ border: none;
380
+ cursor: pointer;
381
+ display: flex;
382
+ align-items: center;
383
+ justify-content: center;
384
+ transition: all 0.3s ease;
385
+ flex-shrink: 0;
386
+ }
387
+
388
+ .send-button:hover:not(:disabled) {
389
+ transform: translateY(-3px);
390
+ box-shadow: 0 12px 25px rgba(102, 126, 234, 0.4);
391
+ }
392
+
393
+ .send-button:active {
394
+ transform: translateY(-1px);
395
+ }
396
+
397
+ .send-button:disabled {
398
+ opacity: 0.6;
399
+ cursor: not-allowed;
400
+ transform: none;
401
+ }
402
+
403
+ .typing-indicator {
404
+ padding: clamp(8px, 2vw, 12px) clamp(12px, 2.5vw, 18px);
405
+ background: #ffffff;
406
+ border-radius: clamp(15px, 3vw, 20px);
407
+ display: inline-flex;
408
+ gap: clamp(4px, 1vw, 6px);
409
+ margin-bottom: clamp(15px, 3vw, 25px);
410
+ box-shadow: var(--shadow-light);
411
+ }
412
+
413
+ .typing-dot {
414
+ width: clamp(6px, 1.5vw, 8px);
415
+ height: clamp(6px, 1.5vw, 8px);
416
+ border-radius: 50%;
417
+ background: #999;
418
+ animation: typing 1.4s infinite;
419
+ }
420
+
421
+ .typing-dot:nth-child(2) {
422
+ animation-delay: 0.2s;
423
+ }
424
+
425
+ .typing-dot:nth-child(3) {
426
+ animation-delay: 0.4s;
427
+ }
428
+
429
+ @keyframes typing {
430
+ 0%, 60%, 100% {
431
+ transform: translateY(0);
432
+ opacity: 0.3;
433
+ }
434
+ 30% {
435
+ transform: translateY(-8px);
436
+ opacity: 1;
437
+ }
438
+ }
439
+
440
+ .loading {
441
+ display: flex;
442
+ align-items: center;
443
+ gap: clamp(6px, 1.2vw, 10px);
444
+ color: #666;
445
+ font-size: clamp(0.8rem, 1.6vw, 0.9rem);
446
+ padding: clamp(8px, 1.5vw, 12px);
447
+ }
448
+
449
+ .loading::after {
450
+ content: '';
451
+ width: clamp(14px, 3vw, 18px);
452
+ height: clamp(14px, 3vw, 18px);
453
+ border: 2px solid #667eea;
454
+ border-top: 2px solid transparent;
455
+ border-radius: 50%;
456
+ animation: spin 1s linear infinite;
457
+ }
458
+
459
+ @keyframes spin {
460
+ to {
461
+ transform: rotate(360deg);
462
+ }
463
+ }
464
+
465
+ /* Responsive Design */
466
+ @media (max-width: 768px) {
467
+ .app-container {
468
+ height: 100vh;
469
+ max-height: none;
470
+ border-radius: 0;
471
+ min-height: 100vh;
472
+ }
473
+
474
+ .config-group {
475
+ grid-template-columns: 1fr;
476
+ gap: clamp(8px, 2vw, 12px);
477
+ }
478
+
479
+ .config-group button {
480
+ width: 100%;
481
+ }
482
+
483
+ .message-content {
484
+ max-width: 90%;
485
+ }
486
+
487
+ .input-group {
488
+ flex-direction: column;
489
+ gap: clamp(8px, 2vw, 12px);
490
+ }
491
+
492
+ .send-button {
493
+ width: 100%;
494
+ height: clamp(45px, 8vh, 50px);
495
+ border-radius: clamp(25px, 5vw, 30px);
496
+ }
497
+ }
498
+
499
+ @media (max-width: 480px) {
500
+ .message {
501
+ gap: clamp(8px, 2vw, 10px);
502
+ }
503
+
504
+ .message-content {
505
+ max-width: 95%;
506
+ font-size: 0.9rem;
507
+ }
508
+
509
+ .input-group textarea {
510
+ min-height: clamp(50px, 10vh, 60px);
511
+ }
512
+ }
513
+
514
+ /* Print styles */
515
+ @media print {
516
+ .api-config,
517
+ .input-container {
518
+ display: none;
519
+ }
520
+
521
+ .app-container {
522
+ box-shadow: none;
523
+ border: 1px solid #ccc;
524
+ }
525
+ }
526
+ </style>
527
+ </head>
528
 
529
+ <body>
530
+ <div class="app-container">
531
+ <div class="header">
532
+ <h1><i class="fas fa-robot"></i> Multi-Provider AI Chat</h1>
533
+ <p>Unified chat interface for multiple AI providers</p>
534
+ <div style="margin-top: 10px;">
535
+ Built with <a href="https://huggingface.co/spaces/akhaliq/anycoder" class="brand-link" target="_blank">
536
+ <i class="fas fa-code"></i> anycoder
537
+ </a>
538
+ </div>
539
+ </div>
540
 
541
+ <div class="api-config">
542
+ <div class="provider-selector">
543
+ <label for="providerSelect"><i class="fas fa-server"></i> AI Provider:</label>
544
+ <select id="providerSelect" onchange="changeProvider()">
545
+ <option value="openai">OpenAI (GPT-3.5/GPT-4)</option>
546
+ <option value="anthropic">Anthropic Claude</option>
547
+ <option value="google">Google Gemini</option>
548
+ <option value="cohere">Cohere</option>
549
+ <option value="together">Together AI</option>
550
+ <option value="groq">Groq</option>
551
+ </select>
552
+ </div>
553
+
554
+ <div class="config-group">
555
+ <input type="text" id="apiKey" placeholder="Enter your API key..." />
556
+ <input type="text" id="modelName" placeholder="Model name" value="gpt-3.5-turbo" />
557
+ <button onclick="saveConfig()">
558
+ <i class="fas fa-save"></i> <span class="button-text">Save</span>
559
+ </button>
560
+ <input type="text" id="apiEndpoint" placeholder="Custom endpoint (optional)" style="grid-column: 1 / -1;" />
561
+ </div>
562
+
563
+ <div class="status-indicator" id="status">
564
+ <i class="fas fa-info-circle"></i> Select provider and enter API key to start
565
+ </div>
566
+ </div>
567
 
568
+ <div class="safety-notice">
569
+ <i class="fas fa-shield-alt"></i>
570
+ <span>Responsible AI usage with built-in safety guidelines across all providers</span>
571
+ </div>
 
 
 
 
 
572
 
573
+ <div class="chat-container">
574
+ <div class="messages" id="messages">
575
+ <div class="message assistant">
576
+ <div class="message-avatar">AI</div>
577
+ <div class="message-content">
578
+ Hello! I'm a multi-provider AI assistant. Select your preferred AI provider above, configure your API key, and let's start chatting!
579
+ <br><br>
580
+ <strong>Supported Providers:</strong><br>
581
+ • <strong>OpenAI:</strong> GPT-3.5, GPT-4, GPT-4 Turbo<br>
582
+ • <strong>Anthropic:</strong> Claude-3 Haiku, Sonnet, Opus<br>
583
+ • <strong>Google:</strong> Gemini Pro, Gemini Pro Vision<br>
584
+ • <strong>Cohere:</strong> Command, Command Light<br>
585
+ • <strong>Together AI:</strong> Various open-source models<br>
586
+ • <strong>Groq:</strong> Ultra-fast inference with Llama, Mixtral
587
+ </div>
588
+ </div>
589
+ </div>
590
+ </div>
591
 
592
+ <div class="input-container">
593
+ <div class="input-group">
594
+ <textarea id="messageInput" placeholder="Type your message here..."
595
+ onkeydown="handleKeyDown(event)"></textarea>
596
+ <button class="send-button" id="sendButton" onclick="sendMessage()">
597
+ <i class="fas fa-paper-plane"></i>
598
+ </button>
599
+ </div>
600
+ </div>
601
+ </div>
602
+
603
+ <script>
604
+ class MultiProviderChatApp {
605
+ constructor() {
606
+ this.currentProvider = 'openai';
607
+ this.apiKey = '';
608
+ this.model = 'gpt-3.5-turbo';
609
+ this.apiEndpoint = '';
610
+ this.isLoading = false;
611
+ this.messages = [];
612
+
613
+ this.providerConfigs = {
614
+ openai: {
615
+ name: 'OpenAI',
616
+ models: ['gpt-3.5-turbo', 'gpt-4', 'gpt-4-turbo-preview'],
617
+ defaultModel: 'gpt-3.5-turbo',
618
+ endpoint: 'https://api.openai.com/v1/chat/completions'
619
+ },
620
+ anthropic: {
621
+ name: 'Anthropic Claude',
622
+ models: ['claude-3-haiku-20240307', 'claude-3-sonnet-20240229', 'claude-3-opus-20240229'],
623
+ defaultModel: 'claude-3-haiku-20240307',
624
+ endpoint: 'https://api.anthropic.com/v1/messages'
625
+ },
626
+ google: {
627
+ name: 'Google Gemini',
628
+ models: ['gemini-pro', 'gemini-pro-vision'],
629
+ defaultModel: 'gemini-pro',
630
+ endpoint: 'https://generativelanguage.googleapis.com/v1beta/models'
631
+ },
632
+ cohere: {
633
+ name: 'Cohere',
634
+ models: ['command', 'command-light', 'command-r', 'command-r-plus'],
635
+ defaultModel: 'command',
636
+ endpoint: 'https://api.cohere.ai/v1/chat'
637
+ },
638
+ together: {
639
+ name: 'Together AI',
640
+ models: ['meta-llama/Llama-2-7b-chat-hf', 'meta-llama/Llama-2-13b-chat-hf', 'mistralai/Mixtral-8x7B-Instruct-v0.1'],
641
+ defaultModel: 'meta-llama/Llama-2-7b-chat-hf',
642
+ endpoint: 'https://api.together.xyz/v1/chat/completions'
643
+ },
644
+ groq: {
645
+ name: 'Groq',
646
+ models: ['llama2-70b-4096', 'mixtral-8x7b-32768', 'gemma-7b-it'],
647
+ defaultModel: 'llama2-70b-4096',
648
+ endpoint: 'https://api.groq.com/openai/v1/chat/completions'
649
+ }
650
+ };
651
+
652
+ this.loadConfig();
653
+ this.initializeEventListeners();
654
+ this.updateUI();
655
+ }
656
+
657
+ loadConfig() {
658
+ this.currentProvider = localStorage.getItem('ai_provider') || 'openai';
659
+ this.apiKey = localStorage.getItem(`${this.currentProvider}_api_key`) || '';
660
+ this.model = localStorage.getItem(`${this.currentProvider}_model`) || this.providerConfigs[this.currentProvider].defaultModel;
661
+ this.apiEndpoint = localStorage.getItem(`${this.currentProvider}_endpoint`) || '';
662
+
663
+ document.getElementById('providerSelect').value = this.currentProvider;
664
+ document.getElementById('apiKey').value = this.apiKey;
665
+ document.getElementById('modelName').value = this.model;
666
+ document.getElementById('apiEndpoint').value = this.apiEndpoint;
667
+
668
+ this.updateStatus();
669
+ }
670
+
671
+ saveConfig() {
672
+ this.currentProvider = document.getElementById('providerSelect').value;
673
+ this.apiKey = document.getElementById('apiKey').value.trim();
674
+ this.model = document.getElementById('modelName').value.trim() || this.providerConfigs[this.currentProvider].defaultModel;
675
+ this.apiEndpoint = document.getElementById('apiEndpoint').value.trim();
676
+
677
+ localStorage.setItem('ai_provider', this.currentProvider);
678
+ localStorage.setItem(`${this.currentProvider}_api_key`, this.apiKey);
679
+ localStorage.setItem(`${this.currentProvider}_model`, this.model);
680
+ localStorage.setItem(`${this.currentProvider}_endpoint`, this.apiEndpoint);
681
+
682
+ this.updateStatus('Configuration saved successfully!', 'success');
683
+ this.showMessage(`Switched to ${this.providerConfigs[this.currentProvider].name}. Model: ${this.model}`, 'assistant');
684
+ }
685
+
686
+ changeProvider() {
687
+ const provider = document.getElementById('providerSelect').value;
688
+ this.currentProvider = provider;
689
+
690
+ // Load saved config for this provider
691
+ this.apiKey = localStorage.getItem(`${provider}_api_key`) || '';
692
+ this.model = localStorage.getItem(`${provider}_model`) || this.providerConfigs[provider].defaultModel;
693
+ this.apiEndpoint = localStorage.getItem(`${provider}_endpoint`) || '';
694
+
695
+ document.getElementById('apiKey').value = this.apiKey;
696
+ document.getElementById('modelName').value = this.model;
697
+ document.getElementById('apiEndpoint').value = this.apiEndpoint;
698
+
699
+ this.updateStatus(`Switched to ${this.providerConfigs[provider].name}`, 'info');
700
+ }
701
+
702
+ updateStatus(message = '', type = 'info') {
703
+ const statusEl = document.getElementById('status');
704
+ const config = this.providerConfigs[this.currentProvider];
705
+ const icon = type === 'error' ? 'exclamation-triangle' :
706
+ type === 'success' ? 'check-circle' : 'info-circle';
707
+ const colorClass = type === 'error' ? 'error' :
708
+ type === 'success' ? 'success' : '';
709
+
710
+ const defaultMessage = this.apiKey ?
711
+ `Ready to chat with ${config.name}!` :
712
+ `Enter ${config.name} API key to start chatting`;
713
+
714
+ statusEl.innerHTML = `<i class="fas fa-${icon}"></i> ${message || defaultMessage}`;
715
+ statusEl.className = `status-indicator ${colorClass}`;
716
+ }
717
+
718
+ async sendMessage() {
719
+ const input = document.getElementById('messageInput');
720
+ const message = input.value.trim();
721
+
722
+ if (!message || this.isLoading) return;
723
+ if (!this.apiKey) {
724
+ this.updateStatus(`Please enter ${this.providerConfigs[this.currentProvider].name} API key`, 'error');
725
+ return;
726
  }
727
 
728
+ input.value = '';
729
+ this.isLoading = true;
730
+ this.updateUI();
731
+
732
+ // Add user message
733
+ this.addMessage(message, 'user');
734
+
735
+ // Show typing indicator
736
+ this.showTypingIndicator();
737
+
738
+ try {
739
+ const response = await this.callAPI(message);
740
+ this.removeTypingIndicator();
741
+ this.addMessage(response, 'assistant');
742
+ } catch (error) {
743
+ this.removeTypingIndicator();
744
+ this.addMessage(`Error: ${error.message}`, 'assistant');
745
+ console.error('API Error:', error);
746
+ } finally {
747
+ this.isLoading = false;
748
+ this.updateUI();
749
  }
750
+ }
751
+
752
+ async callAPI(message) {
753
+ const config = this.providerConfigs[this.currentProvider];
754
+ const endpoint = this.apiEndpoint || config.endpoint;
755
+
756
+ let requestBody, headers, responseData;
757
+
758
+ // Build request based on provider
759
+ switch (this.currentProvider) {
760
+ case 'openai':
761
+ requestBody = this.buildOpenAIRequest(message);
762
+ headers = {
763
+ 'Content-Type': 'application/json',
764
+ 'Authorization': `Bearer ${this.apiKey}`
765
+ };
766
+ break;
767
+
768
+ case 'anthropic':
769
+ requestBody = this.buildAnthropicRequest(message);
770
+ headers = {
771
+ 'Content-Type': 'application/json',
772
+ 'x-api-key': this.apiKey,
773
+ 'anthropic-version': '2023-06-01'
774
+ };
775
+ break;
776
+
777
+ case 'google':
778
+ return await this.callGoogleAPI(message);
779
+
780
+ case 'cohere':
781
+ requestBody = this.buildCohereRequest(message);
782
+ headers = {
783
+ 'Content-Type': 'application/json',
784
+ 'Authorization': `Bearer ${this.apiKey}`
785
+ };
786
+ break;
787
+
788
+ case 'together':
789
+ requestBody = this.buildTogetherRequest(message);
790
+ headers = {
791
+ 'Content-Type': 'application/json',
792
+ 'Authorization': `Bearer ${this.apiKey}`
793
+ };
794
+ break;
795
+
796
+ case 'groq':
797
+ requestBody = this.buildGroqRequest(message);
798
+ headers = {
799
+ 'Content-Type': 'application/json',
800
+ 'Authorization': `Bearer ${this.apiKey}`
801
+ };
802
+ break;
803
  }
804
 
805
+ const response = await fetch(endpoint, {
806
+ method: 'POST',
807
+ headers,
808
+ body: JSON.stringify(requestBody)
809
+ });
 
 
 
 
 
810
 
811
+ if (!response.ok) {
812
+ const error = await response.json();
813
+ throw new Error(error.error?.message || error.message || 'API request failed');
 
 
 
814
  }
815
 
816
+ responseData = await response.json();
817
+ return this.parseResponse(responseData);
818
+ }
819
+
820
+ buildOpenAIRequest(message) {
821
+ return {
822
+ model: this.model,
823
+ messages: [
824
+ { role: 'system', content: this.getSystemPrompt() },
825
+ ...this.messages.slice(-10),
826
+ { role: 'user', content: message }
827
+ ],
828
+ max_tokens: 1000,
829
+ temperature: 0.7
830
+ };
831
+ }
832
+
833
+ buildAnthropicRequest(message) {
834
+ return {
835
+ model: this.model,
836
+ max_tokens: 1000,
837
+ messages: [
838
+ { role: 'user', content: `${this.getSystemPrompt()}\n\nHuman: ${message}\n\nAssistant:` }
839
+ ]
840
+ };
841
+ }
842
+
843
+ async callGoogleAPI(message) {
844
+ const endpoint = `${this.providerConfigs.google.endpoint}/${this.model}:generateContent?key=${this.apiKey}`;
845
+ const response = await fetch(endpoint, {
846
+ method: 'POST',
847
+ headers: { 'Content-Type': 'application/json' },
848
+ body: JSON.stringify({
849
+ contents: [{
850
+ parts: [{ text: `${this.getSystemPrompt()}\n\nUser: ${message}\n\nAssistant:` }]
851
+ }]
852
+ })
853
+ });
854
 
855
+ if (!response.ok) {
856
+ const error = await response.json();
857
+ throw new Error(error.error?.message || 'API request failed');
 
858
  }
859
 
860
+ const data = await response.json();
861
+ return data.candidates[0].content.parts[0].text;
862
+ }
863
+
864
+ buildCohereRequest(message) {
865
+ return {
866
+ message: `${this.getSystemPrompt()}\n\nUser: ${message}`,
867
+ chat_history: this.messages.slice(-10).map(msg => ({
868
+ role: msg.role === 'assistant' ? 'CHATBOT' : 'USER',
869
+ message: msg.content
870
+ }))
871
+ };
872
+ }
873
+
874
+ buildTogetherRequest(message) {
875
+ return {
876
+ model: this.model,
877
+ messages: [
878
+ { role: 'system', content: this.getSystemPrompt() },
879
+ ...this.messages.slice(-10),
880
+ { role: 'user', content: message }
881
+ ],
882
+ max_tokens: 1000,
883
+ temperature: 0.7
884
+ };
885
+ }
886
+
887
+ buildGroqRequest(message) {
888
+ return {
889
+ model: this.model,
890
+ messages: [
891
+ { role: 'system', content: this.getSystemPrompt() },
892
+ ...this.messages.slice(-10),
893
+ { role: 'user', content: message }
894
+ ],
895
+ max_tokens: 1000,
896
+ temperature: 0.7
897
+ };
898
+ }
899
+
900
+ parseResponse(data) {
901
+ switch (this.currentProvider) {
902
+ case 'openai':
903
+ case 'together':
904
+ case 'groq':
905
+ return data.choices[0].message.content;
906
 
907
+ case 'anthropic':
908
+ return data.content[0].text;
 
 
909
 
910
+ case 'cohere':
911
+ return data.text;
 
912
 
913
+ default:
914
+ return 'Response parsed successfully';
 
915
  }
916
+ }
917
+
918
+ getSystemPrompt() {
919
+ return `You are a helpful AI assistant. Always be helpful, harmless, and honest.
920
+ Follow these guidelines:
921
+ - Provide accurate and helpful information
922
+ - Be respectful and considerate
923
+ - Admit when you don't know something
924
+ - Avoid harmful, illegal, or unethical content
925
+ - Keep responses concise and relevant`;
926
+ }
927
+
928
+ addMessage(content, sender) {
929
+ const messagesEl = document.getElementById('messages');
930
+ const messageEl = document.createElement('div');
931
+ messageEl.className = `message ${sender}`;
932
+
933
+ const avatar = sender === 'user' ? 'U' : 'AI';
934
+
935
+ messageEl.innerHTML = `
936
+ <div class="message-avatar">${avatar}</div>
937
+ <div class="message-content">${this.formatMessage(content)}</div>
938
+ `;
939
+
940
+ messagesEl.appendChild(messageEl);
941
+ messagesEl.scrollTop = messagesEl.scrollHeight;
942
+
943
+ if (sender === 'assistant') {
944
+ this.messages.push({ role: 'assistant', content: content });
945
  }
946
+ }
947
+
948
+ formatMessage(content) {
949
+ return content
950
+ .replace(/\n/g, '<br>')
951
+ .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
952
+ .replace(/\*(.*?)\*/g, '<em>$1</em>')
953
+ .replace(/`(.*?)`/g, '<code style="background: #f4f4f4; padding: 2px 4px; border-radius: 3px; font-size: 0.9em;">$1</code>');
954
+ }
955
+
956
+ showTypingIndicator() {
957
+ const messagesEl = document.getElementById('messages');
958
+ const typingEl = document.createElement('div');
959
+ typingEl.className = 'message assistant';
960
+ typingEl.id = 'typing-indicator';
961
+ typingEl.innerHTML = `
962
+ <div class="message-avatar">AI</div>
963
+ <div class="message-content">
964
+ <div class="typing-indicator">
965
+ <div class="typing-dot"></div>
966
+ <div class="typing-dot"></div>
967
+ <div class="typing-dot"></div>
968
  </div>
969
+ </div>
970
+ `;
971
+ messagesEl.appendChild(typingEl);
972
+ messagesEl.scrollTop = messagesEl.scrollHeight;
973
+ }
974
+
975
+ removeTypingIndicator() {
976
+ const typingEl = document.getElementById('typing-indicator');
977
+ if (typingEl) {
978
+ typingEl.remove();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
979
  }
980
+ }
981
+
982
+ updateUI() {
983
+ const sendButton = document.getElementById('sendButton');
984
+ const input = document.getElementById('messageInput');
985
+
986
+ sendButton.disabled = this.isLoading || !this.apiKey;
987
+ input.disabled = this.isLoading;
988
+ }
989
+
990
+ initializeEventListeners() {
991
+ document.getElementById('messageInput').addEventListener('input', () => {
992
+ this.updateUI();
993
  });
994
+ }
995
+ }
996
+
997
+ // Global functions
998
+ let chatApp;
999
+
1000
+ function saveConfig() {
1001
+ chatApp.saveConfig();
1002
+ }
1003
+
1004
+ function changeProvider() {
1005
+ chatApp.changeProvider();
1006
+ }
1007
+
1008
+ function sendMessage() {
1009
+ chatApp.sendMessage();
1010
+ }
1011
+
1012
+ function handleKeyDown(event) {
1013
+ if (event.key === 'Enter' && !event.shiftKey) {
1014
+ event.preventDefault();
1015
+ sendMessage();
1016
+ }
1017
+ }
1018
+
1019
+ // Initialize the application
1020
+ document.addEventListener('DOMContentLoaded', () => {
1021
+ chatApp = new MultiProviderChatApp();
1022
+ });
1023
+ </script>
1024
  </body>
1025
  </html>