jasvir-singh1021 commited on
Commit
4f19981
·
verified ·
1 Parent(s): 8e8e99c

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +80 -752
src/streamlit_app.py CHANGED
@@ -1,752 +1,80 @@
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>Document Parser - LlamaIndex & GPT-4</title>
7
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
8
- <style>
9
- :root {
10
- --primary-color: #1a1a2e;
11
- --secondary-color: #16213e;
12
- --accent-color: #0f3460;
13
- --highlight-color: #e94560;
14
- --text-primary: #ffffff;
15
- --text-secondary: #b3b3b3;
16
- --bg-primary: #0a0a0a;
17
- --bg-secondary: #1a1a1a;
18
- --bg-tertiary: #2a2a2a;
19
- --success-color: #4ade80;
20
- --warning-color: #fbbf24;
21
- --error-color: #ef4444;
22
- --border-radius: 12px;
23
- --shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.1);
24
- --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
25
- --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.2);
26
- --transition: all 0.3s ease;
27
- }
28
-
29
- * {
30
- margin: 0;
31
- padding: 0;
32
- box-sizing: border-box;
33
- }
34
-
35
- body {
36
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
37
- background: var(--bg-primary);
38
- color: var(--text-primary);
39
- line-height: 1.6;
40
- min-height: 100vh;
41
- overflow-x: hidden;
42
- }
43
-
44
- .container {
45
- max-width: 1200px;
46
- margin: 0 auto;
47
- padding: 2rem;
48
- }
49
-
50
- .header {
51
- text-align: center;
52
- margin-bottom: 3rem;
53
- animation: fadeInDown 0.6s ease;
54
- }
55
-
56
- .header h1 {
57
- font-size: 2.5rem;
58
- font-weight: 700;
59
- background: linear-gradient(135deg, var(--highlight-color), #ff6b6b);
60
- -webkit-background-clip: text;
61
- -webkit-text-fill-color: transparent;
62
- margin-bottom: 0.5rem;
63
- }
64
-
65
- .header p {
66
- color: var(--text-secondary);
67
- font-size: 1.2rem;
68
- }
69
-
70
- .card {
71
- background: var(--bg-secondary);
72
- border-radius: var(--border-radius);
73
- padding: 2rem;
74
- box-shadow: var(--shadow-lg);
75
- border: 1px solid #333;
76
- margin-bottom: 2rem;
77
- transition: var(--transition);
78
- }
79
-
80
- .card:hover {
81
- transform: translateY(-2px);
82
- box-shadow: 0 15px 25px rgba(0, 0, 0, 0.3);
83
- }
84
-
85
- .input-group {
86
- margin-bottom: 1.5rem;
87
- }
88
-
89
- .input-label {
90
- display: block;
91
- margin-bottom: 0.5rem;
92
- font-weight: 600;
93
- color: var(--text-primary);
94
- }
95
-
96
- .input-field {
97
- width: 100%;
98
- padding: 0.75rem 1rem;
99
- background: var(--bg-tertiary);
100
- border: 1px solid #444;
101
- border-radius: var(--border-radius);
102
- color: var(--text-primary);
103
- font-size: 1rem;
104
- transition: var(--transition);
105
- }
106
-
107
- .input-field:focus {
108
- outline: none;
109
- border-color: var(--highlight-color);
110
- box-shadow: 0 0 0 3px rgba(233, 69, 96, 0.2);
111
- }
112
-
113
- .slider-container {
114
- margin-bottom: 2rem;
115
- }
116
-
117
- .slider {
118
- width: 100%;
119
- height: 6px;
120
- background: var(--bg-tertiary);
121
- border-radius: 3px;
122
- outline: none;
123
- -webkit-appearance: none;
124
- cursor: pointer;
125
- }
126
-
127
- .slider::-webkit-slider-thumb {
128
- -webkit-appearance: none;
129
- appearance: none;
130
- width: 20px;
131
- height: 20px;
132
- background: var(--highlight-color);
133
- border-radius: 50%;
134
- cursor: pointer;
135
- transition: var(--transition);
136
- }
137
-
138
- .slider::-webkit-slider-thumb:hover {
139
- transform: scale(1.2);
140
- }
141
-
142
- .file-upload {
143
- position: relative;
144
- display: inline-block;
145
- width: 100%;
146
- }
147
-
148
- .file-upload-label {
149
- display: flex;
150
- align-items: center;
151
- justify-content: center;
152
- padding: 2rem;
153
- border: 2px dashed #555;
154
- border-radius: var(--border-radius);
155
- cursor: pointer;
156
- transition: var(--transition);
157
- background: var(--bg-tertiary);
158
- }
159
-
160
- .file-upload-label:hover {
161
- border-color: var(--highlight-color);
162
- background: var(--bg-secondary);
163
- }
164
-
165
- .file-upload-input {
166
- display: none;
167
- }
168
-
169
- .upload-icon {
170
- font-size: 3rem;
171
- color: var(--highlight-color);
172
- margin-bottom: 1rem;
173
- }
174
-
175
- .btn {
176
- display: inline-flex;
177
- align-items: center;
178
- justify-content: center;
179
- padding: 0.75rem 1.5rem;
180
- background: var(--highlight-color);
181
- color: white;
182
- border: none;
183
- border-radius: var(--border-radius);
184
- font-size: 1rem;
185
- font-weight: 600;
186
- cursor: pointer;
187
- transition: var(--transition);
188
- text-decoration: none;
189
- gap: 0.5rem;
190
- }
191
-
192
- .btn:hover {
193
- background: #ff5252;
194
- transform: translateY(-1px);
195
- box-shadow: var(--shadow-md);
196
- }
197
-
198
- .btn-secondary {
199
- background: var(--bg-tertiary);
200
- color: var(--text-primary);
201
- border: 1px solid #555;
202
- }
203
-
204
- .btn-secondary:hover {
205
- background: #444;
206
- }
207
-
208
- .btn-danger {
209
- background: var(--error-color);
210
- }
211
-
212
- .btn-danger:hover {
213
- background: #dc2626;
214
- }
215
-
216
- .chat-container {
217
- max-height: 600px;
218
- overflow-y: auto;
219
- padding: 1rem;
220
- background: var(--bg-secondary);
221
- border-radius: var(--border-radius);
222
- margin-bottom: 1rem;
223
- }
224
-
225
- .chat-message {
226
- display: flex;
227
- margin-bottom: 1rem;
228
- animation: fadeInUp 0.3s ease;
229
- }
230
-
231
- .chat-message.user {
232
- justify-content: flex-end;
233
- }
234
-
235
- .chat-message.assistant {
236
- justify-content: flex-start;
237
- }
238
-
239
- .chat-bubble {
240
- max-width: 70%;
241
- padding: 1rem;
242
- border-radius: var(--border-radius);
243
- word-wrap: break-word;
244
- position: relative;
245
- }
246
-
247
- .chat-message.user .chat-bubble {
248
- background: linear-gradient(135deg, var(--highlight-color), #ff6b6b);
249
- color: white;
250
- border-bottom-right-radius: 4px;
251
- }
252
-
253
- .chat-message.assistant .chat-bubble {
254
- background: var(--bg-tertiary);
255
- color: var(--text-primary);
256
- border-bottom-left-radius: 4px;
257
- }
258
-
259
- .loading {
260
- display: inline-block;
261
- position: relative;
262
- width: 20px;
263
- height: 20px;
264
- }
265
-
266
- .loading div {
267
- position: absolute;
268
- top: 8px;
269
- width: 6px;
270
- height: 6px;
271
- border-radius: 50%;
272
- background: var(--text-secondary);
273
- animation-timing-function: cubic-bezier(0, 1, 1, 0);
274
- }
275
-
276
- .loading div:nth-child(1) {
277
- left: 0;
278
- animation: loading1 0.6s infinite;
279
- }
280
-
281
- .loading div:nth-child(2) {
282
- left: 6px;
283
- animation: loading2 0.6s infinite;
284
- }
285
-
286
- .loading div:nth-child(3) {
287
- left: 12px;
288
- animation: loading2 0.6s infinite;
289
- }
290
-
291
- .loading div:nth-child(4) {
292
- left: 18px;
293
- animation: loading3 0.6s infinite;
294
- }
295
-
296
- @keyframes loading1 {
297
- 0% { transform: scale(0); }
298
- 100% { transform: scale(1); }
299
- }
300
-
301
- @keyframes loading3 {
302
- 0% { transform: scale(1); }
303
- 100% { transform: scale(0); }
304
- }
305
-
306
- @keyframes loading2 {
307
- 0% { transform: translate(0, 0); }
308
- 100% { transform: translate(6px, 0); }
309
- }
310
-
311
- @keyframes fadeInDown {
312
- from {
313
- opacity: 0;
314
- transform: translateY(-20px);
315
- }
316
- to {
317
- opacity: 1;
318
- transform: translateY(0);
319
- }
320
- }
321
-
322
- @keyframes fadeInUp {
323
- from {
324
- opacity: 0;
325
- transform: translateY(20px);
326
- }
327
- to {
328
- opacity: 1;
329
- transform: translateY(0);
330
- }
331
- }
332
-
333
- .grid {
334
- display: grid;
335
- grid-template-columns: 1fr 1fr;
336
- gap: 1rem;
337
- }
338
-
339
- .alert {
340
- padding: 1rem;
341
- border-radius: var(--border-radius);
342
- margin-bottom: 1rem;
343
- animation: fadeInUp 0.3s ease;
344
- }
345
-
346
- .alert-info {
347
- background: rgba(59, 130, 246, 0.1);
348
- border: 1px solid rgba(59, 130, 246, 0.3);
349
- color: #93c5fd;
350
- }
351
-
352
- .alert-warning {
353
- background: rgba(245, 158, 11, 0.1);
354
- border: 1px solid rgba(245, 158, 11, 0.3);
355
- color: #fbbf24;
356
- }
357
-
358
- .alert-error {
359
- background: rgba(239, 68, 68, 0.1);
360
- border: 1px solid rgba(239, 68, 68, 0.3);
361
- color: #f87171;
362
- }
363
-
364
- .file-list {
365
- margin-top: 1rem;
366
- }
367
-
368
- .file-item {
369
- display: flex;
370
- align-items: center;
371
- justify-content: space-between;
372
- padding: 0.5rem 1rem;
373
- background: var(--bg-tertiary);
374
- border-radius: var(--border-radius);
375
- margin-bottom: 0.5rem;
376
- animation: fadeInUp 0.3s ease;
377
- }
378
-
379
- .file-name {
380
- display: flex;
381
- align-items: center;
382
- gap: 0.5rem;
383
- color: var(--text-primary);
384
- }
385
-
386
- .file-icon {
387
- color: var(--highlight-color);
388
- }
389
-
390
- .remove-file {
391
- background: none;
392
- border: none;
393
- color: var(--text-secondary);
394
- cursor: pointer;
395
- padding: 0.25rem;
396
- transition: var(--transition);
397
- }
398
-
399
- .remove-file:hover {
400
- color: var(--error-color);
401
- transform: scale(1.1);
402
- }
403
-
404
- .references {
405
- margin-top: 1rem;
406
- padding: 1rem;
407
- background: var(--bg-tertiary);
408
- border-radius: var(--border-radius);
409
- border-left: 3px solid var(--highlight-color);
410
- }
411
-
412
- .references summary {
413
- cursor: pointer;
414
- font-weight: 600;
415
- color: var(--highlight-color);
416
- }
417
-
418
- .references details {
419
- margin-top: 0.5rem;
420
- }
421
-
422
- @media (max-width: 768px) {
423
- .container {
424
- padding: 1rem;
425
- }
426
-
427
- .header h1 {
428
- font-size: 2rem;
429
- }
430
-
431
- .grid {
432
- grid-template-columns: 1fr;
433
- }
434
-
435
- .chat-bubble {
436
- max-width: 85%;
437
- }
438
- }
439
-
440
- .spinner {
441
- display: inline-block;
442
- width: 20px;
443
- height: 20px;
444
- border: 3px solid rgba(233, 69, 96, 0.3);
445
- border-radius: 50%;
446
- border-top-color: var(--highlight-color);
447
- animation: spin 1s ease-in-out infinite;
448
- }
449
-
450
- @keyframes spin {
451
- to { transform: rotate(360deg); }
452
- }
453
- </style>
454
- </head>
455
- <body>
456
- <div class="container">
457
- <div class="header">
458
- <h1><i class="fas fa-file-alt"></i> Document Parser</h1>
459
- <p>Upload documents and chat with GPT-4 powered AI</p>
460
- </div>
461
-
462
- <div class="card">
463
- <div class="input-group">
464
- <label class="input-label" for="apiKey">
465
- <i class="fas fa-key"></i> OpenAI API Key
466
- </label>
467
- <input type="password" id="apiKey" class="input-field" placeholder="Enter your OpenAI API key">
468
- </div>
469
-
470
- <div class="slider-container">
471
- <label class="input-label">
472
- <i class="fas fa-thermometer-half"></i> Model Temperature
473
- <span id="tempValue">0.0</span>
474
- </label>
475
- <input type="range" id="temperature" class="slider" min="0" max="1" step="0.1" value="0">
476
- </div>
477
-
478
- <div class="input-group">
479
- <label class="input-label">
480
- <i class="fas fa-cloud-upload-alt"></i> Upload Documents
481
- </label>
482
- <div class="file-upload">
483
- <label class="file-upload-label" for="fileInput">
484
- <div>
485
- <i class="fas fa-cloud-upload-alt upload-icon"></i>
486
- <p>Click to upload or drag files here</p>
487
- <small>Supported formats: PDF, DOCX, DOC, TXT, RTF, HTML</small>
488
- </div>
489
- </label>
490
- <input type="file" id="fileInput" class="file-upload-input" multiple accept=".pdf,.docx,.doc,.txt,.rtf,.html">
491
- </div>
492
- <div id="fileList" class="file-list"></div>
493
- </div>
494
-
495
- <div style="text-align: right; margin-top: 1rem;">
496
- <button class="btn btn-danger" onclick="clearConversation()">
497
- <i class="fas fa-trash"></i> Clear History
498
- </button>
499
- </div>
500
- </div>
501
-
502
- <div id="chatSection" style="display: none;">
503
- <div class="card">
504
- <div class="input-group">
505
- <label class="input-label">
506
- <i class="fas fa-question-circle"></i> Ask a question
507
- </label>
508
- <div style="display: flex; gap: 0.5rem;">
509
- <input type="text" id="questionInput" class="input-field" placeholder="Ask about your documents...">
510
- <button class="btn" onclick="askQuestion()">
511
- <i class="fas fa-paper-plane"></i>
512
- </button>
513
- </div>
514
- </div>
515
-
516
- <div id="chatContainer" class="chat-container">
517
- <div id="chatMessages"></div>
518
- </div>
519
-
520
- <div class="grid">
521
- <button class="btn btn-secondary" onclick="downloadConversation('txt')">
522
- <i class="fas fa-download"></i> Download TXT
523
- </button>
524
- <button class="btn btn-secondary" onclick="downloadConversation('json')">
525
- <i class="fas fa-download"></i> Download JSON
526
- </button>
527
- </div>
528
- </div>
529
- </div>
530
- </div>
531
-
532
- <script>
533
- let uploadedFiles = [];
534
- let conversationHistory = [];
535
- let isProcessing = false;
536
-
537
- // Temperature slider
538
- const tempSlider = document.getElementById('temperature');
539
- const tempValue = document.getElementById('tempValue');
540
- tempSlider.addEventListener('input', (e) => {
541
- tempValue.textContent = e.target.value;
542
- });
543
-
544
- // File upload handling
545
- const fileInput = document.getElementById('fileInput');
546
- const fileList = document.getElementById('fileList');
547
-
548
- fileInput.addEventListener('change', (e) => {
549
- const files = Array.from(e.target.files);
550
- uploadedFiles = [...uploadedFiles, ...files];
551
- renderFileList();
552
- document.getElementById('chatSection').style.display = 'block';
553
- });
554
-
555
- function renderFileList() {
556
- fileList.innerHTML = '';
557
- uploadedFiles.forEach((file, index) => {
558
- const fileItem = document.createElement('div');
559
- fileItem.className = 'file-item';
560
- fileItem.innerHTML = `
561
- <div class="file-name">
562
- <i class="fas fa-file file-icon"></i>
563
- <span>${file.name}</span>
564
- </div>
565
- <button class="remove-file" onclick="removeFile(${index})">
566
- <i class="fas fa-times"></i>
567
- </button>
568
- `;
569
- fileList.appendChild(fileItem);
570
- });
571
- }
572
-
573
- function removeFile(index) {
574
- uploadedFiles.splice(index, 1);
575
- renderFileList();
576
- if (uploadedFiles.length === 0) {
577
- document.getElementById('chatSection').style.display = 'none';
578
- }
579
- }
580
-
581
- async function askQuestion() {
582
- const apiKey = document.getElementById('apiKey').value;
583
- const question = document.getElementById('questionInput').value;
584
- const temperature = parseFloat(document.getElementById('temperature').value);
585
-
586
- if (!apiKey) {
587
- showAlert('Please enter your OpenAI API key', 'error');
588
- return;
589
- }
590
-
591
- if (!question.trim()) {
592
- showAlert('Please enter a question', 'warning');
593
- return;
594
- }
595
-
596
- if (uploadedFiles.length === 0) {
597
- showAlert('Please upload some documents first', 'warning');
598
- return;
599
- }
600
-
601
- if (isProcessing) return;
602
-
603
- isProcessing = true;
604
- addMessage('user', question);
605
- document.getElementById('questionInput').value = '';
606
-
607
- const formData = new FormData();
608
- formData.append('apiKey', apiKey);
609
- formData.append('question', question);
610
- formData.append('temperature', temperature);
611
- uploadedFiles.forEach(file => {
612
- formData.append('documents', file);
613
- });
614
-
615
- addMessage('assistant', '<div class="spinner"></div> Processing...');
616
-
617
- try {
618
- // Simulate processing delay
619
- await new Promise(resolve => setTimeout(resolve, 2000));
620
-
621
- // Mock response
622
- const mockResponse = {
623
- answer: `Based on the uploaded documents, here's what I found: ${question}`,
624
- sources: [
625
- { page: 1, text: "This is a preview of the source text..." }
626
- ]
627
- };
628
-
629
- updateLastMessage(mockResponse.answer);
630
- } catch (error) {
631
- updateLastMessage('Error processing your request. Please try again.');
632
- showAlert('Error processing request', 'error');
633
- }
634
-
635
- isProcessing = false;
636
- }
637
-
638
- function addMessage(role, content) {
639
- const message = { role, content };
640
- conversationHistory.push(message);
641
-
642
- const chatMessages = document.getElementById('chatMessages');
643
- const messageDiv = document.createElement('div');
644
- messageDiv.className = `chat-message ${role}`;
645
- messageDiv.innerHTML = `
646
- <div class="chat-bubble">
647
- ${content}
648
- </div>
649
- `;
650
- chatMessages.appendChild(messageDiv);
651
- chatMessages.scrollTop = chatMessages.scrollHeight;
652
- }
653
-
654
- function updateLastMessage(content) {
655
- const chatMessages = document.getElementById('chatMessages');
656
- const lastMessage = chatMessages.lastElementChild;
657
- if (lastMessage) {
658
- lastMessage.querySelector('.chat-bubble').innerHTML = content;
659
- }
660
-
661
- if (conversationHistory.length > 0) {
662
- conversationHistory[conversationHistory.length - 1].content = content;
663
- }
664
- }
665
-
666
- function clearConversation() {
667
- conversationHistory = [];
668
- document.getElementById('chatMessages').innerHTML = '';
669
- document.getElementById('questionInput').value = '';
670
- }
671
-
672
- function downloadConversation(format) {
673
- let content, filename, mimeType;
674
-
675
- if (format === 'txt') {
676
- content = conversationHistory.map(msg => `${msg.role.toUpperCase()}:\n${msg.content}\n`).join('\n');
677
- filename = 'conversation.txt';
678
- mimeType = 'text/plain';
679
- } else {
680
- content = JSON.stringify(conversationHistory, null, 2);
681
- filename = 'conversation.json';
682
- mimeType = 'application/json';
683
- }
684
-
685
- const blob = new Blob([content], { type: mimeType });
686
- const url = URL.createObjectURL(blob);
687
- const a = document.createElement('a');
688
- a.href = url;
689
- a.download = filename;
690
- a.click();
691
- URL.revokeObjectURL(url);
692
- }
693
-
694
- function showAlert(message, type = 'info') {
695
- const alertDiv = document.createElement('div');
696
- alertDiv.className = `alert alert-${type}`;
697
- alertDiv.textContent = message;
698
- document.body.insertBefore(alertDiv, document.body.firstChild);
699
-
700
- setTimeout(() => {
701
- alertDiv.remove();
702
- }, 5000);
703
- }
704
-
705
- // Drag and drop functionality
706
- const uploadLabel = document.querySelector('.file-upload-label');
707
-
708
- ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
709
- uploadLabel.addEventListener(eventName, preventDefaults, false);
710
- });
711
-
712
- function preventDefaults(e) {
713
- e.preventDefault();
714
- e.stopPropagation();
715
- }
716
-
717
- ['dragenter', 'dragover'].forEach(eventName => {
718
- uploadLabel.addEventListener(eventName, highlight);
719
- });
720
-
721
- ['dragleave', 'drop'].forEach(eventName => {
722
- uploadLabel.addEventListener(eventName, unhighlight);
723
- });
724
-
725
- function highlight() {
726
- uploadLabel.style.borderColor = '#e94560';
727
- uploadLabel.style.background = '#111';
728
- }
729
-
730
- function unhighlight() {
731
- uploadLabel.style.borderColor = '#555';
732
- uploadLabel.style.background = '#2a2a2a';
733
- }
734
-
735
- uploadLabel.addEventListener('drop', handleDrop);
736
-
737
- function handleDrop(e) {
738
- const files = Array.from(e.dataTransfer.files);
739
- uploadedFiles = [...uploadedFiles, ...files];
740
- renderFileList();
741
- document.getElementById('chatSection').style.display = 'block';
742
- }
743
-
744
- // Enter key to send question
745
- document.getElementById('questionInput').addEventListener('keypress', (e) => {
746
- if (e.key === 'Enter') {
747
- askQuestion();
748
- }
749
- });
750
- </script>
751
- </body>
752
- </html>
 
1
+ import streamlit as st
2
+ import json
3
+ from io import StringIO
4
+
5
+ # Configure page
6
+ st.set_page_config(page_title="Document Parser", layout="wide")
7
+
8
+ # Session state for conversation history
9
+ if "conversation" not in st.session_state:
10
+ st.session_state.conversation = []
11
+
12
+ # Sidebar: API key + temperature
13
+ with st.sidebar:
14
+ st.title("⚙️ Settings")
15
+ api_key = st.text_input("🔑 OpenAI API Key", type="password")
16
+ temperature = st.slider("🔥 Model Temperature", 0.0, 1.0, 0.0, 0.1)
17
+
18
+ # Main header
19
+ st.title("📄 Document Parser")
20
+ st.markdown("Upload documents and chat with a GPT-4 powered assistant.")
21
+
22
+ # Upload documents
23
+ uploaded_files = st.file_uploader(
24
+ "📤 Upload Documents (PDF, DOCX, TXT, etc.)",
25
+ type=["pdf", "docx", "doc", "txt", "rtf", "html"],
26
+ accept_multiple_files=True
27
+ )
28
+
29
+ if uploaded_files:
30
+ st.success(f"{len(uploaded_files)} document(s) uploaded.")
31
+ else:
32
+ st.info("Please upload at least one document to continue.")
33
+
34
+ # Input for user question
35
+ question = st.text_input("💬 Ask a question about your documents:")
36
+
37
+ # Send button
38
+ if st.button("🚀 Ask") and question and uploaded_files and api_key:
39
+ with st.spinner("Processing..."):
40
+ # Here you'd pass files + question + temperature + API key to your backend
41
+ # Replace this with real logic (e.g., LlamaIndex + OpenAI)
42
+ mock_answer = f"🧠 Based on the uploaded documents, here's a mock answer to: '{question}'"
43
+
44
+ # Add to conversation history
45
+ st.session_state.conversation.append({"role": "user", "content": question})
46
+ st.session_state.conversation.append({"role": "assistant", "content": mock_answer})
47
+
48
+ # Display conversation
49
+ if st.session_state.conversation:
50
+ st.markdown("## 🧾 Conversation")
51
+ for msg in st.session_state.conversation:
52
+ if msg["role"] == "user":
53
+ st.markdown(f"**You:** {msg['content']}")
54
+ else:
55
+ st.markdown(f"**Assistant:** {msg['content']}")
56
+
57
+ st.markdown("---")
58
+
59
+ # Download buttons
60
+ col1, col2 = st.columns(2)
61
+
62
+ with col1:
63
+ if st.button("🗑️ Clear Conversation"):
64
+ st.session_state.conversation = []
65
+ st.experimental_rerun()
66
+
67
+ with col2:
68
+ format = st.selectbox("Download Format", ["TXT", "JSON"])
69
+ if format == "TXT":
70
+ content = "\n\n".join(
71
+ f"{msg['role'].capitalize()}:\n{msg['content']}" for msg in st.session_state.conversation
72
+ )
73
+ mime = "text/plain"
74
+ filename = "conversation.txt"
75
+ else:
76
+ content = json.dumps(st.session_state.conversation, indent=2)
77
+ mime = "application/json"
78
+ filename = "conversation.json"
79
+
80
+ st.download_button("📥 Download", content, filename=filename, mime=mime)