pvanand commited on
Commit
e61c33a
·
verified ·
1 Parent(s): 1ddf5a9

Create digiyatra

Browse files
Files changed (1) hide show
  1. static/digiyatra +436 -0
static/digiyatra ADDED
@@ -0,0 +1,436 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>AI Chatbot</title>
7
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
8
+ <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/uuid/8.3.2/uuid.min.js"></script>
10
+ <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
11
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.1/styles/default.min.css">
12
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.1/highlight.min.js"></script>
13
+ <style>
14
+ :root {
15
+ --accent-color: #003366; /* Navy blue */
16
+ }
17
+ body {
18
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
19
+ line-height: 1.6;
20
+ margin: 0 auto;
21
+ padding: 0px;
22
+ background-color: #f5f7fa;
23
+ color: #333;
24
+ }
25
+ h2 {
26
+ color: var(--accent-color);
27
+ border-bottom: 2px solid var(--accent-color);
28
+ padding-bottom: 10px;
29
+ margin-top: 0;
30
+ }
31
+ #app {
32
+ max-width: 800px;
33
+ margin: 0 auto;
34
+ padding: 10px;
35
+ }
36
+ .chat-container {
37
+ background-color: #ffffff;
38
+ border-radius: 10px;
39
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
40
+ overflow: hidden;
41
+ display: flex;
42
+ flex-direction: column;
43
+ height: 99vh;
44
+ }
45
+ .messages {
46
+ flex-grow: 1;
47
+ overflow-y: auto;
48
+ padding: 20px;
49
+ display: flex;
50
+ flex-direction: column;
51
+ }
52
+ .message {
53
+ max-width: 80%;
54
+ margin-bottom: 20px;
55
+ padding: 12px 16px;
56
+ border-radius: 18px;
57
+ font-size: 14px;
58
+ line-height: 1.4;
59
+ word-wrap: break-word;
60
+ overflow-wrap: break-word;
61
+ }
62
+ .user-message {
63
+ background-color: #364e67;
64
+ color: #ffffff;
65
+ align-self: flex-end;
66
+ }
67
+ .bot-message {
68
+ background-color: #f0f2f5;
69
+ color: #333;
70
+ align-self: flex-start;
71
+ background-color: #ffffff;
72
+ padding: 30px;
73
+ border-radius: 8px;
74
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
75
+ overflow-wrap: break-word;
76
+ word-wrap: break-word;
77
+ word-break: break-word;
78
+ }
79
+ .input-area {
80
+ display: flex;
81
+ padding: 10px;
82
+ background-color: #ffffff;
83
+ border-top: 1px solid #e0e0e0;
84
+ }
85
+ #user-input {
86
+ flex-grow: 1;
87
+ padding: 12px;
88
+ border: 1px solid #d0d0d0;
89
+ border-radius: 20px;
90
+ font-size: 14px;
91
+ outline: none;
92
+ }
93
+ .send-button {
94
+ background-color: #007bff;
95
+ color: #ffffff;
96
+ border: none;
97
+ border-radius: 50%;
98
+ width: 40px;
99
+ height: 40px;
100
+ margin-left: 10px;
101
+ cursor: pointer;
102
+ display: flex;
103
+ align-items: center;
104
+ justify-content: center;
105
+ transition: background-color 0.3s;
106
+ }
107
+ .send-button:hover {
108
+ background-color: #0056b3;
109
+ }
110
+ .send-button svg {
111
+ width: 20px;
112
+ height: 20px;
113
+ }
114
+ .reset-button {
115
+ background-color: #f44336;
116
+ color: #ffffff;
117
+ border: none;
118
+ border-radius: 20px;
119
+ padding: 8px 16px;
120
+ margin-left: 10px;
121
+ cursor: pointer;
122
+ font-size: 14px;
123
+ transition: background-color 0.3s;
124
+ }
125
+ .reset-button:hover {
126
+ background-color: #d32f2f;
127
+ }
128
+ .option-buttons {
129
+ display: flex;
130
+ flex-wrap: wrap;
131
+ gap: 10px;
132
+ margin-bottom: 15px;
133
+ }
134
+ .option-button {
135
+ background-color: #f0f2f5;
136
+ border: 1px solid #d0d0d0;
137
+ border-radius: 20px;
138
+ padding: 8px 16px;
139
+ font-size: 14px;
140
+ cursor: pointer;
141
+ transition: background-color 0.3s, transform 0.1s;
142
+ }
143
+ .option-button:hover {
144
+ background-color: #e4e6e9;
145
+ transform: translateY(-2px);
146
+ }
147
+ .option-button:active {
148
+ transform: translateY(0);
149
+ }
150
+ .option-button.selected {
151
+ background-color: #9fb4cd;
152
+ border-color: #c9d9ec;
153
+ }
154
+ .play-button {
155
+ background-color: #4CAF50;
156
+ border: none;
157
+ color: white;
158
+ padding: 10px 20px;
159
+ text-align: center;
160
+ text-decoration: none;
161
+ display: inline-block;
162
+ font-size: 16px;
163
+ margin: 4px 2px;
164
+ cursor: pointer;
165
+ border-radius: 4px;
166
+ }
167
+ .audio-button {
168
+ background: none;
169
+ border: none;
170
+ cursor: pointer;
171
+ padding: 0;
172
+ margin-top: 10px;
173
+ }
174
+ .audio-button svg {
175
+ width: 24px;
176
+ height: 24px;
177
+ }
178
+ .audio-button:focus {
179
+ outline: none;
180
+ }
181
+ @media (max-width: 600px) {
182
+ #app {
183
+ padding: 10px;
184
+ }
185
+ .chat-container {
186
+ height: 90vh;
187
+ }
188
+ .message {
189
+ max-width: 90%;
190
+ }
191
+ }
192
+ </style>
193
+ </head>
194
+ <body>
195
+ <div id="app">
196
+ <div class="chat-container">
197
+ <div class="messages" ref="messageContainer">
198
+ <div v-for="(message, index) in messages" :key="index"
199
+ :class="['message', message.type === 'user' ? 'user-message' : 'bot-message']">
200
+ <div v-html="message.content"></div>
201
+ <button v-if="message.type === 'bot' && message.audio" @click="toggleAudio(index)" class="audio-button">
202
+ <svg v-if="!message.isPlaying" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
203
+ <polygon points="5 3 19 12 5 21 5 3"></polygon>
204
+ </svg>
205
+ <svg v-else xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
206
+ <rect x="6" y="4" width="4" height="16"></rect>
207
+ <rect x="14" y="4" width="4" height="16"></rect>
208
+ </svg>
209
+ </button>
210
+ </div>
211
+ </div>
212
+ <div class="input-area">
213
+ <input type="text" id="user-input" v-model="userInput" @keyup.enter="sendMessage" placeholder="Type your message...">
214
+ <button class="send-button" @click="sendMessage">
215
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
216
+ <line x1="22" y1="2" x2="11" y2="13"></line>
217
+ <polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>
218
+ </svg>
219
+ </button>
220
+ <button class="reset-button" @click="resetConversation">Reset</button>
221
+ </div>
222
+ </div>
223
+ </div>
224
+
225
+ <script>
226
+ marked.setOptions({
227
+ highlight: function (code, lang) {
228
+ if (lang && hljs.getLanguage(lang)) {
229
+ return hljs.highlight(code, { language: lang }).value;
230
+ } else {
231
+ return hljs.highlightAuto(code).value;
232
+ }
233
+ },
234
+ sanitize: false
235
+ });
236
+
237
+ const app = new Vue({
238
+ el: '#app',
239
+ data: {
240
+ messages: [],
241
+ userInput: '',
242
+ selectedOptions: {},
243
+ conversationId: '',
244
+ currentAudio: null,
245
+ },
246
+ methods: {
247
+ async sendMessage() {
248
+ if (!this.userInput.trim()) return;
249
+
250
+ this.messages.push({ type: 'user', content: marked.parse(this.userInput) });
251
+ const message = this.userInput;
252
+ this.userInput = '';
253
+ this.selectedOptions = {};
254
+
255
+ try {
256
+ const response = await fetch('https://pvanand-rag-chat.hf.space/chat/', {
257
+ method: 'POST',
258
+ headers: {
259
+ 'Content-Type': 'application/json',
260
+ 'X-API-Key': '44d5c2ac18ced6fc25c1e57dcd06fc0b31fb4ad97bf56e67540671a647465df4'
261
+ },
262
+ body: JSON.stringify({
263
+ query: message,
264
+ index_id: 'digi_yatra_cleaned_twitter_data_v0_20_rows',
265
+ model_id: 'openai/gpt-4o-mini',
266
+ conversation_id: this.conversationId,
267
+ user_id: 'string'
268
+ enable_followup: true
269
+ })
270
+ });
271
+
272
+ const reader = response.body.getReader();
273
+ let rawResponse = '';
274
+ let jsonResponse = null;
275
+ let streamingIndex = this.messages.push({ type: 'bot', content: 'Thinking...' }) - 1;
276
+
277
+ while (true) {
278
+ const { done, value } = await reader.read();
279
+ if (done) break;
280
+
281
+ const chunk = new TextDecoder().decode(value);
282
+ if (chunk.startsWith('<json>')) {
283
+ jsonResponse = JSON.parse(chunk.slice(6));
284
+ } else {
285
+ rawResponse += chunk;
286
+ this.$set(this.messages, streamingIndex, { type: 'bot', content: marked.parse(rawResponse) });
287
+ }
288
+ }
289
+
290
+ this.messages.splice(streamingIndex, 1);
291
+ this.renderParsedResponse(rawResponse, jsonResponse);
292
+ } catch (error) {
293
+ console.error('Error:', error);
294
+ this.messages.push({ type: 'bot', content: 'An error occurred while processing your request.' });
295
+ }
296
+ this.$nextTick(() => this.scrollToBottom());
297
+ },
298
+ renderParsedResponse(rawResponse, jsonResponse) {
299
+ let botResponse = rawResponse + '\n\n';
300
+ let responseForAudio = rawResponse.replace(/#/g, '');
301
+
302
+ if (jsonResponse && jsonResponse.clarification) {
303
+ jsonResponse.clarification.forEach((item, questionIndex) => {
304
+ botResponse += `**${item.question}**\n\n`;
305
+ botResponse += '<div class="option-buttons">';
306
+ item.options.forEach((option, optionIndex) => {
307
+ const escapedOption = option.replace(/'/g, "\\'");
308
+ botResponse += `<button class="option-button" onclick="app.toggleOption('${escapedOption}', ${questionIndex}, ${optionIndex})">${option}</button>`;
309
+ });
310
+ botResponse += '</div>\n\n';
311
+ });
312
+ }
313
+
314
+ if (botResponse) {
315
+ const markedContent = marked.parse(botResponse);
316
+ const messageIndex = this.messages.push({
317
+ type: 'bot',
318
+ content: markedContent,
319
+ audio: null,
320
+ isPlaying: false
321
+ }) - 1;
322
+ this.$nextTick(async () => {
323
+ document.querySelectorAll('pre code').forEach((block) => {
324
+ hljs.highlightBlock(block);
325
+ });
326
+ this.scrollToBottom();
327
+ this.updateButtonStates();
328
+
329
+ if (responseForAudio) {
330
+ const audioUrl = await this.convertToSpeech(responseForAudio);
331
+ if (audioUrl) {
332
+ this.$set(this.messages[messageIndex], 'audio', audioUrl);
333
+ this.$forceUpdate();
334
+ }
335
+ }
336
+ });
337
+ }
338
+ },
339
+ toggleOption(option, questionIndex, optionIndex) {
340
+ if (!this.selectedOptions[questionIndex]) {
341
+ this.$set(this.selectedOptions, questionIndex, []);
342
+ }
343
+
344
+ const index = this.selectedOptions[questionIndex].indexOf(option);
345
+ if (index > -1) {
346
+ this.selectedOptions[questionIndex].splice(index, 1);
347
+ } else {
348
+ this.selectedOptions[questionIndex].push(option);
349
+ }
350
+
351
+ this.updateInputFromSelectedOptions();
352
+ this.updateButtonStates();
353
+ },
354
+ updateInputFromSelectedOptions() {
355
+ this.userInput = Object.entries(this.selectedOptions)
356
+ .map(([questionIndex, options]) =>
357
+ `Q${parseInt(questionIndex) + 1}: ${options.join(', ')}`)
358
+ .join(' | ');
359
+ this.$nextTick(() => document.getElementById('user-input').focus());
360
+ },
361
+ updateButtonStates() {
362
+ Object.entries(this.selectedOptions).forEach(([questionIndex, options]) => {
363
+ const buttons = document.querySelectorAll(`.option-buttons:nth-of-type(${parseInt(questionIndex) + 1}) .option-button`);
364
+ buttons.forEach((button) => {
365
+ if (options.includes(button.textContent)) {
366
+ button.classList.add('selected');
367
+ } else {
368
+ button.classList.remove('selected');
369
+ }
370
+ });
371
+ });
372
+ },
373
+ resetConversation() {
374
+ this.conversationId = uuid.v4();
375
+ this.messages = [{ type: 'bot', content: 'Conversation reset. How can I help you?' }];
376
+ this.selectedOptions = {};
377
+ this.userInput = '';
378
+ },
379
+ scrollToBottom() {
380
+ const container = this.$refs.messageContainer;
381
+ container.scrollTop = container.scrollHeight;
382
+ },
383
+ async convertToSpeech(text) {
384
+ const voice = 'en-US-JennyNeural';
385
+ const encodedText = encodeURIComponent(text);
386
+ try {
387
+ const response = await fetch(`https://pvanand-audio-chat.hf.space/tts?text=${encodedText}&voice=${voice}`, {
388
+ method: 'GET',
389
+ headers: {
390
+ 'accept': 'application/json'
391
+ }
392
+ });
393
+ if (!response.ok) {
394
+ throw new Error(`HTTP error! status: ${response.status}`);
395
+ }
396
+ const blob = await response.blob();
397
+ return URL.createObjectURL(blob);
398
+ } catch (error) {
399
+ console.error('Error:', error);
400
+ return null;
401
+ }
402
+ },
403
+ toggleAudio(index) {
404
+ const message = this.messages[index];
405
+ if (this.currentAudio && this.currentAudio !== message.audioElement) {
406
+ this.currentAudio.pause();
407
+ this.messages.forEach(m => {
408
+ if (m.audioElement === this.currentAudio) {
409
+ m.isPlaying = false;
410
+ }
411
+ });
412
+ }
413
+ if (!message.audioElement) {
414
+ message.audioElement = new Audio(message.audio);
415
+ message.audioElement.addEventListener('ended', () => {
416
+ message.isPlaying = false;
417
+ this.$forceUpdate();
418
+ });
419
+ }
420
+ if (message.isPlaying) {
421
+ message.audioElement.pause();
422
+ } else {
423
+ message.audioElement.play();
424
+ this.currentAudio = message.audioElement;
425
+ }
426
+ message.isPlaying = !message.isPlaying;
427
+ this.$forceUpdate();
428
+ }
429
+ },
430
+ mounted() {
431
+ this.resetConversation();
432
+ }
433
+ });
434
+ </script>
435
+ </body>
436
+ </html>