siddhartharya commited on
Commit
056fd41
Β·
verified Β·
1 Parent(s): b79ca65

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +385 -395
app.py CHANGED
@@ -2,7 +2,6 @@
2
 
3
  import gradio as gr
4
  from bs4 import BeautifulSoup
5
- import requests
6
  from sentence_transformers import SentenceTransformer
7
  import faiss
8
  import numpy as np
@@ -13,6 +12,7 @@ import base64
13
  import logging
14
  import os
15
  import sys
 
16
 
17
  # Import OpenAI library
18
  import openai
@@ -35,7 +35,7 @@ logger.addHandler(console_handler)
35
  # Initialize models and variables
36
  logger.info("Initializing models and variables")
37
  embedding_model = SentenceTransformer('all-MiniLM-L6-v2')
38
- faiss_index = None
39
  bookmarks = []
40
  fetch_cache = {}
41
 
@@ -101,7 +101,7 @@ async def fetch_url_info(session, bookmark):
101
 
102
  try:
103
  logger.info(f"Fetching URL info for: {url}")
104
- async with session.get(url, timeout=5) as response:
105
  bookmark['etag'] = response.headers.get('ETag', 'N/A')
106
  bookmark['status_code'] = response.status
107
 
@@ -122,7 +122,11 @@ async def fetch_url_info(session, bookmark):
122
  elif meta_description and meta_description.get('content'):
123
  description = meta_description.get('content')
124
  else:
125
- description = ''
 
 
 
 
126
 
127
  bookmark['description'] = description
128
  logger.info(f"Fetched description for {url}")
@@ -142,12 +146,12 @@ async def fetch_url_info(session, bookmark):
142
  return bookmark
143
 
144
  # Asynchronous processing of bookmarks
145
- async def process_bookmarks_async(bookmarks):
146
  logger.info("Processing bookmarks asynchronously")
147
  try:
148
  async with aiohttp.ClientSession() as session:
149
  tasks = []
150
- for bookmark in bookmarks:
151
  task = asyncio.ensure_future(fetch_url_info(session, bookmark))
152
  tasks.append(task)
153
  await asyncio.gather(*tasks)
@@ -156,12 +160,34 @@ async def process_bookmarks_async(bookmarks):
156
  logger.error(f"Error in asynchronous processing of bookmarks: {e}")
157
  raise
158
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  # Generate summary for a bookmark
160
  def generate_summary(bookmark):
161
  description = bookmark.get('description', '')
162
  if description:
163
  bookmark['summary'] = description
164
  else:
 
165
  title = bookmark.get('title', '')
166
  if title:
167
  bookmark['summary'] = title
@@ -170,6 +196,33 @@ def generate_summary(bookmark):
170
  logger.info(f"Generated summary for bookmark: {bookmark.get('url')}")
171
  return bookmark
172
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
  # Assign category to a bookmark
174
  def assign_category(bookmark):
175
  if bookmark.get('dead_link'):
@@ -177,62 +230,40 @@ def assign_category(bookmark):
177
  logger.info(f"Assigned category 'Dead Link' to bookmark: {bookmark.get('url')}")
178
  return bookmark
179
 
180
- summary = bookmark.get('summary', '').lower()
181
- assigned_category = 'Uncategorized'
182
-
183
- # Keywords associated with each category
184
- category_keywords = {
185
- "Social Media": ["social media", "networking", "friends", "connect", "posts", "profile"],
186
- "News and Media": ["news", "journalism", "media", "headlines", "breaking news"],
187
- "Education and Learning": ["education", "learning", "courses", "tutorial", "university", "academy", "study"],
188
- "Entertainment": ["entertainment", "movies", "tv shows", "games", "comics", "fun"],
189
- "Shopping and E-commerce": ["shopping", "e-commerce", "buy", "sell", "marketplace", "deals", "store"],
190
- "Finance and Banking": ["finance", "banking", "investment", "money", "economy", "stock", "trading"],
191
- "Technology": ["technology", "tech", "gadgets", "software", "computers", "innovation"],
192
- "Health and Fitness": ["health", "fitness", "medical", "wellness", "exercise", "diet"],
193
- "Travel and Tourism": ["travel", "tourism", "destinations", "hotels", "flights", "vacation"],
194
- "Food and Recipes": ["food", "recipes", "cooking", "cuisine", "restaurant", "dining"],
195
- "Sports": ["sports", "scores", "teams", "athletics", "matches", "leagues"],
196
- "Arts and Culture": ["arts", "culture", "museum", "gallery", "exhibition", "artistic"],
197
- "Government and Politics": ["government", "politics", "policy", "election", "public service"],
198
- "Business and Economy": ["business", "corporate", "industry", "economy", "markets"],
199
- "Science and Research": ["science", "research", "experiment", "laboratory", "study", "scientific"],
200
- "Personal Blogs and Journals": ["blog", "journal", "personal", "diary", "thoughts", "opinions"],
201
- "Job Search and Careers": ["jobs", "careers", "recruitment", "resume", "employment", "hiring"],
202
- "Music and Audio": ["music", "audio", "songs", "albums", "artists", "bands"],
203
- "Videos and Movies": ["video", "movies", "film", "clips", "trailers", "cinema"],
204
- "Reference and Knowledge Bases": ["reference", "encyclopedia", "dictionary", "wiki", "knowledge", "information"],
205
- }
206
-
207
- for category, keywords in category_keywords.items():
208
- for keyword in keywords:
209
- if re.search(r'\b' + re.escape(keyword) + r'\b', summary):
210
- assigned_category = category
211
- logger.info(f"Assigned category '{assigned_category}' to bookmark: {bookmark.get('url')}")
212
- break
213
- if assigned_category != 'Uncategorized':
214
- break
215
-
216
- bookmark['category'] = assigned_category
217
- if assigned_category == 'Uncategorized':
218
- logger.info(f"No matching category found for bookmark: {bookmark.get('url')}")
219
  return bookmark
220
 
221
  # Vectorize summaries and build FAISS index
222
- def vectorize_and_index(bookmarks):
223
- logger.info("Vectorizing summaries and building FAISS index")
 
224
  try:
225
- summaries = [bookmark['summary'] for bookmark in bookmarks]
226
- embeddings = embedding_model.encode(summaries)
227
- dimension = embeddings.shape[1]
228
- faiss_idx = faiss.IndexFlatL2(dimension)
229
- faiss_idx.add(np.array(embeddings))
230
- logger.info("FAISS index built successfully")
231
- return faiss_idx, embeddings
232
  except Exception as e:
233
  logger.error(f"Error in vectorizing and indexing: {e}")
234
  raise
235
 
 
 
 
 
 
 
 
 
 
 
236
  # Generate HTML display for bookmarks
237
  def display_bookmarks():
238
  logger.info("Generating HTML display for bookmarks")
@@ -270,34 +301,65 @@ def display_bookmarks():
270
  return cards
271
 
272
  # Process the uploaded file
273
- def process_uploaded_file(file):
274
  global bookmarks, faiss_index
275
  logger.info("Processing uploaded file")
276
  if file is None:
277
  logger.warning("No file uploaded")
278
- return "Please upload a bookmarks HTML file.", '', gr.update(choices=[]), display_bookmarks()
 
 
 
 
 
 
 
279
  try:
280
  file_content = file.decode('utf-8')
281
  except UnicodeDecodeError as e:
282
  logger.error(f"Error decoding the file: {e}")
283
- return "Error decoding the file. Please ensure it's a valid HTML file.", '', gr.update(choices=[]), display_bookmarks()
 
 
 
 
 
 
284
 
285
  try:
286
  bookmarks = parse_bookmarks(file_content)
287
  except Exception as e:
288
  logger.error(f"Error parsing bookmarks: {e}")
289
- return "Error parsing the bookmarks HTML file.", '', gr.update(choices=[]), display_bookmarks()
 
 
 
 
 
 
290
 
291
  if not bookmarks:
292
  logger.warning("No bookmarks found in the uploaded file")
293
- return "No bookmarks found in the uploaded file.", '', gr.update(choices=[]), display_bookmarks()
 
 
 
 
 
 
294
 
295
  # Asynchronously fetch bookmark info
296
  try:
297
  asyncio.run(process_bookmarks_async(bookmarks))
298
  except Exception as e:
299
  logger.error(f"Error processing bookmarks asynchronously: {e}")
300
- return "Error processing bookmarks.", '', gr.update(choices=[]), display_bookmarks()
 
 
 
 
 
 
301
 
302
  # Generate summaries and assign categories
303
  for bookmark in bookmarks:
@@ -305,70 +367,127 @@ def process_uploaded_file(file):
305
  assign_category(bookmark)
306
 
307
  try:
308
- faiss_index, embeddings = vectorize_and_index(bookmarks)
309
  except Exception as e:
310
  logger.error(f"Error building FAISS index: {e}")
311
- return "Error building search index.", '', gr.update(choices=[]), display_bookmarks()
 
 
 
 
 
 
 
 
 
 
312
 
313
  message = f"βœ… Successfully processed {len(bookmarks)} bookmarks."
314
  logger.info(message)
315
  bookmark_html = display_bookmarks()
316
 
317
- # Update bookmark_selector choices
318
  choices = [f"{i+1}. {bookmark['title']} (Category: {bookmark['category']})" for i, bookmark in enumerate(bookmarks)]
319
- bookmark_selector_update = gr.update(choices=choices, value=[])
320
 
321
- # Update bookmark_display_manage
322
- bookmark_display_manage_update = display_bookmarks()
323
 
324
- return message, bookmark_html, bookmark_selector_update, bookmark_display_manage_update
 
 
 
 
 
 
325
 
326
  # Delete selected bookmarks
327
- def delete_selected_bookmarks(selected_indices):
328
- global bookmarks, faiss_index
329
  if not selected_indices:
330
- return "⚠️ No bookmarks selected.", gr.update(choices=[]), display_bookmarks()
331
- indices = [int(s.split('.')[0])-1 for s in selected_indices]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
332
  indices = sorted(indices, reverse=True)
333
  for idx in indices:
334
- if 0 <= idx < len(bookmarks):
335
- logger.info(f"Deleting bookmark at index {idx + 1}")
336
- bookmarks.pop(idx)
337
- if bookmarks:
338
- faiss_index, embeddings = vectorize_and_index(bookmarks)
339
- else:
340
- faiss_index = None
341
  message = "πŸ—‘οΈ Selected bookmarks deleted successfully."
342
  logger.info(message)
343
- # Update bookmark_selector choices
344
- choices = [f"{i+1}. {bookmark['title']} (Category: {bookmark['category']})" for i, bookmark in enumerate(bookmarks)]
345
- bookmark_selector_update = gr.update(choices=choices, value=[])
346
- # Update bookmarks display
347
  bookmarks_html = display_bookmarks()
348
- return message, bookmark_selector_update, bookmarks_html
 
 
 
 
 
 
 
349
 
350
  # Edit category of selected bookmarks
351
- def edit_selected_bookmarks_category(selected_indices, new_category):
352
  if not selected_indices:
353
- return "⚠️ No bookmarks selected.", '', gr.update()
 
 
 
 
 
 
354
  if not new_category:
355
- return "⚠️ No new category selected.", '', gr.update()
356
- indices = [int(s.split('.')[0])-1 for s in selected_indices]
357
- for idx in indices:
358
- if 0 <= idx < len(bookmarks):
359
- bookmarks[idx]['category'] = new_category
360
- logger.info(f"Updated category for bookmark {idx + 1} to {new_category}")
 
 
 
 
 
 
 
 
 
 
 
361
  message = "✏️ Category updated for selected bookmarks."
362
  logger.info(message)
363
- # Update bookmark_selector choices
364
- choices = [f"{i+1}. {bookmark['title']} (Category: {bookmark['category']})" for i, bookmark in enumerate(bookmarks)]
365
- bookmark_selector_update = gr.update(choices=choices, value=[])
366
- # Update bookmarks display
367
  bookmarks_html = display_bookmarks()
368
- return message, bookmark_selector_update, bookmarks_html
 
 
 
 
 
 
 
369
 
370
  # Export bookmarks to HTML
371
- def export_bookmarks():
 
372
  if not bookmarks:
373
  logger.warning("No bookmarks to export")
374
  return "⚠️ No bookmarks to export."
@@ -394,25 +513,46 @@ def export_bookmarks():
394
  logger.error(f"Error exporting bookmarks: {e}")
395
  return "⚠️ Error exporting bookmarks."
396
 
397
- # Chatbot response using Groq Cloud API
398
- def chatbot_response(user_query):
399
  if not GROQ_API_KEY:
400
  logger.warning("GROQ_API_KEY not set.")
401
  return "⚠️ API key not set. Please set the GROQ_API_KEY environment variable in the Hugging Face Space settings."
402
 
 
403
  if not bookmarks:
404
  logger.warning("No bookmarks available for chatbot")
405
  return "⚠️ No bookmarks available. Please upload and process your bookmarks first."
406
 
407
  logger.info(f"Chatbot received query: {user_query}")
408
 
409
- # Prepare the prompt for the LLM
410
  try:
411
- # Limit the number of bookmarks to prevent exceeding token limits
412
- max_bookmarks = 50 # Adjust as needed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
413
  bookmark_data = ""
414
- for idx, bookmark in enumerate(bookmarks[:max_bookmarks]):
415
- bookmark_data += f"{idx+1}. Title: {bookmark['title']}\nURL: {bookmark['url']}\nSummary: {bookmark['summary']}\n\n"
416
 
417
  # Construct the prompt
418
  prompt = f"""
@@ -421,15 +561,15 @@ You are an assistant that helps users find relevant bookmarks from their collect
421
  User Query:
422
  {user_query}
423
 
424
- Bookmarks:
425
  {bookmark_data}
426
 
427
- Please identify the most relevant bookmarks that match the user's query. Provide a concise list including the index, title, URL, and a brief summary.
428
  """
429
 
430
  # Call the Groq Cloud API via the OpenAI client
431
  response = openai.ChatCompletion.create(
432
- model='llama3-8b-8192', # Verify this model name with Groq Cloud API documentation
433
  messages=[
434
  {"role": "system", "content": "You help users find relevant bookmarks based on their queries."},
435
  {"role": "user", "content": prompt}
@@ -453,366 +593,216 @@ Please identify the most relevant bookmarks that match the user's query. Provide
453
  def build_app():
454
  try:
455
  logger.info("Building Gradio app")
456
- with gr.Blocks() as demo:
457
- # Embed the CSS and Theme Toggle Switch
458
- gr.HTML(
459
- """
460
- <style>
461
- /* Define CSS Variables for Themes */
462
- :root {
463
- --background-color: #FFFFFF;
464
- --text-color: #000000;
465
- --success-color: #4CAF50;
466
- --error-color: #D32F2F;
467
- --card-shadow: rgba(0, 0, 0, 0.2);
468
- }
469
-
470
- .dark-theme {
471
- --background-color: #121212;
472
- --text-color: #FFFFFF;
473
- --success-color: #81C784;
474
- --error-color: #E57373;
475
- --card-shadow: rgba(255, 255, 255, 0.2);
476
- }
477
-
478
- /* Apply Background and Text Colors */
479
- body {
480
- background-color: var(--background-color);
481
- color: var(--text-color);
482
- transition: background-color 0.3s, color 0.3s;
483
- }
484
-
485
- /* Card Styles */
486
- .card {
487
- box-shadow: 0 4px 8px 0 var(--card-shadow);
488
- transition: box-shadow 0.3s, background-color 0.3s;
489
- background-color: var(--background-color);
490
- padding: 10px;
491
- margin: 10px;
492
- border-radius: 5px;
493
- }
494
-
495
- .card:hover {
496
- box-shadow: 0 8px 16px 0 var(--card-shadow);
497
- }
498
-
499
- /* Toggle Switch Styles */
500
- .theme-toggle {
501
- display: flex;
502
- align-items: center;
503
- justify-content: flex-end;
504
- padding: 10px;
505
- }
506
-
507
- .switch {
508
- position: relative;
509
- display: inline-block;
510
- width: 60px;
511
- height: 34px;
512
- }
513
-
514
- .switch input {
515
- opacity: 0;
516
- width: 0;
517
- height: 0;
518
- }
519
-
520
- .slider {
521
- position: absolute;
522
- cursor: pointer;
523
- top: 0;
524
- left: 0;
525
- right: 0;
526
- bottom: 0;
527
- background-color: #ccc;
528
- transition: .4s;
529
- border-radius: 34px;
530
- }
531
-
532
- .slider:before {
533
- position: absolute;
534
- content: "";
535
- height: 26px;
536
- width: 26px;
537
- left: 4px;
538
- bottom: 4px;
539
- background-color: white;
540
- transition: .4s;
541
- border-radius: 50%;
542
- }
543
-
544
- input:checked + .slider {
545
- background-color: #2196F3;
546
- }
547
-
548
- input:checked + .slider:before {
549
- transform: translateX(26px);
550
- }
551
-
552
- /* Gradio Components Styling */
553
- .gradio-container {
554
- background-color: var(--background-color);
555
- color: var(--text-color);
556
- transition: background-color 0.3s, color 0.3s;
557
- }
558
-
559
- /* Ensure All Text Elements Use var(--text-color) */
560
- body, p, h1, h2, h3, h4, h5, h6, span, a, label, button, input, textarea, select {
561
- color: var(--text-color);
562
- transition: color 0.3s, background-color 0.3s;
563
- }
564
-
565
- /* Links Styling */
566
- a {
567
- color: var(--text-color);
568
- text-decoration: underline;
569
- }
570
-
571
- a:hover {
572
- color: var(--success-color);
573
- }
574
-
575
- /* Buttons Styling */
576
- button, .gr-button {
577
- background-color: var(--success-color);
578
- color: var(--background-color);
579
- border: none;
580
- padding: 10px 20px;
581
- text-align: center;
582
- text-decoration: none;
583
- display: inline-block;
584
- font-size: 16px;
585
- margin: 4px 2px;
586
- cursor: pointer;
587
- border-radius: 4px;
588
- transition: background-color 0.3s, color 0.3s;
589
- }
590
-
591
- button:hover, .gr-button:hover {
592
- background-color: var(--error-color);
593
- }
594
-
595
- /* Inputs and Selects Styling */
596
- input, textarea, select, .gr-textbox, .gr-dropdown, .gr-file {
597
- background-color: var(--background-color);
598
- color: var(--text-color);
599
- border: 1px solid var(--text-color);
600
- border-radius: 4px;
601
- padding: 8px;
602
- transition: background-color 0.3s, color 0.3s, border-color 0.3s;
603
- }
604
-
605
- /* Checkbox and Radio Buttons Styling */
606
- input[type="checkbox"], input[type="radio"] {
607
- accent-color: var(--success-color);
608
- }
609
-
610
- /* Gradio-specific Class Overrides */
611
- .gradio-container .gradio-block,
612
- .gradio-container .gradio-row,
613
- .gradio-container .gradio-column {
614
- background-color: var(--background-color);
615
- color: var(--text-color);
616
- }
617
-
618
- /* Ensure Gradio Tabs Respect the Theme */
619
- .gradio-container .tab-title {
620
- color: var(--text-color);
621
- }
622
-
623
- .gradio-container .tab-title.selected {
624
- border-bottom: 2px solid var(--success-color);
625
- }
626
-
627
- /* Additional Styling for HTML Components within Gradio */
628
- .gradio-container .markdown p,
629
- .gradio-container .markdown h1,
630
- .gradio-container .markdown h2,
631
- .gradio-container .markdown h3,
632
- .gradio-container .markdown h4,
633
- .gradio-container .markdown h5,
634
- .gradio-container .markdown h6 {
635
- color: var(--text-color);
636
- }
637
-
638
- .gradio-container .html-content a {
639
- color: var(--text-color);
640
- }
641
-
642
- .gradio-container .html-content a:hover {
643
- color: var(--success-color);
644
- }
645
- </style>
646
-
647
- <div class="theme-toggle">
648
- <label class="switch">
649
- <input type="checkbox" id="theme-checkbox">
650
- <span class="slider"></span>
651
- </label>
652
- <span style="margin-left: 10px;">Dark Mode</span>
653
- </div>
654
- <script>
655
- // Check for saved user preference on load
656
- if (localStorage.getItem('theme') === 'dark') {
657
- document.getElementById('theme-checkbox').checked = true;
658
- document.body.classList.add('dark-theme');
659
- }
660
-
661
- const toggleSwitch = document.getElementById('theme-checkbox');
662
- toggleSwitch.addEventListener('change', function() {
663
- if(this.checked) {
664
- document.body.classList.add('dark-theme');
665
- localStorage.setItem('theme', 'dark');
666
- } else {
667
- document.body.classList.remove('dark-theme');
668
- localStorage.setItem('theme', 'light');
669
- }
670
- });
671
- </script>
672
- """
673
- )
674
 
675
  # General Overview
676
  gr.Markdown("""
677
- # πŸ“š SmartMarks - AI Browser Bookmarks Manager
 
 
678
 
679
- Welcome to **SmartMarks**, your intelligent assistant for managing browser bookmarks. SmartMarks leverages AI to help you organize, search, and interact with your bookmarks seamlessly. Whether you're looking to categorize your links, retrieve information quickly, or maintain an updated list, SmartMarks has you covered.
680
 
681
- ---
682
 
683
- ## πŸš€ **How to Use SmartMarks**
684
 
685
- SmartMarks is divided into three main sections:
 
 
686
 
687
- 1. **πŸ“‚ Upload and Process Bookmarks:** Import your existing bookmarks and let SmartMarks analyze and categorize them for you.
688
- 2. **πŸ’¬ Chat with Bookmarks:** Interact with your bookmarks using natural language queries to find relevant links effortlessly.
689
- 3. **πŸ› οΈ Manage Bookmarks:** View, edit, delete, and export your bookmarks with ease.
690
 
691
- Navigate through the tabs to explore each feature in detail.
692
- """)
 
693
 
694
  # Upload and Process Bookmarks Tab
695
  with gr.Tab("Upload and Process Bookmarks"):
696
  gr.Markdown("""
697
- ## πŸ“‚ **Upload and Process Bookmarks**
698
 
699
- ### πŸ“ **Steps to Upload and Process:**
700
 
701
- 1. **πŸ”½ Upload Bookmarks File:**
702
- - Click on the **"Upload Bookmarks HTML File"** button.
703
- - Select your browser's exported bookmarks HTML file from your device.
704
 
705
- 2. **βš™οΈ Process Bookmarks:**
706
- - After uploading, click on the **"Process Bookmarks"** button.
707
- - SmartMarks will parse your bookmarks, fetch additional information, generate summaries, and categorize each link based on predefined categories.
708
 
709
- 3. **πŸ“„ View Processed Bookmarks:**
710
- - Once processing is complete, your bookmarks will be displayed in an organized and visually appealing format below.
711
- """)
712
 
713
  upload = gr.File(label="πŸ“ Upload Bookmarks HTML File", type='binary')
714
  process_button = gr.Button("βš™οΈ Process Bookmarks")
715
  output_text = gr.Textbox(label="βœ… Output", interactive=False)
716
  bookmark_display = gr.HTML(label="πŸ“„ Bookmarks")
717
 
718
- # Initialize Manage Bookmarks components
719
- bookmark_selector = gr.CheckboxGroup(label="βœ… Select Bookmarks", choices=[])
720
- bookmark_display_manage = gr.HTML(label="πŸ“„ Manage Bookmarks Display")
721
-
722
  process_button.click(
723
  process_uploaded_file,
724
- inputs=upload,
725
- outputs=[output_text, bookmark_display, bookmark_selector, bookmark_display_manage]
726
  )
727
 
728
  # Chat with Bookmarks Tab
729
  with gr.Tab("Chat with Bookmarks"):
730
  gr.Markdown("""
731
- ## πŸ’¬ **Chat with Bookmarks**
 
 
732
 
733
- ### πŸ€– **How to Interact:**
 
734
 
735
- 1. **✍️ Enter Your Query:**
736
- - In the **"Ask about your bookmarks"** textbox, type your question or keyword related to your bookmarks. For example, "Do I have any bookmarks about GenerativeAI?"
737
 
738
- 2. **πŸ“¨ Submit Your Query:**
739
- - Click the **"Send"** button to submit your query.
740
 
741
- 3. **πŸ“ˆ Receive AI-Driven Responses:**
742
- - SmartMarks will analyze your query and provide relevant bookmarks that match your request, making it easier to find specific links without manual searching.
743
- """)
744
 
745
- user_input = gr.Textbox(label="✍️ Ask about your bookmarks", placeholder="e.g., Do I have any bookmarks about GenerativeAI?")
746
- chat_output = gr.Textbox(label="πŸ’¬ Chatbot Response", interactive=False)
747
- chat_button = gr.Button("πŸ“¨ Send")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
748
 
 
749
  chat_button.click(
750
  chatbot_response,
751
- inputs=user_input,
752
- outputs=chat_output
753
  )
754
 
755
  # Manage Bookmarks Tab
756
  with gr.Tab("Manage Bookmarks"):
757
  gr.Markdown("""
758
- ## πŸ› οΈ **Manage Bookmarks**
759
 
760
- ### πŸ—‚οΈ **Features:**
761
 
762
- 1. **πŸ‘οΈ View Bookmarks:**
763
- - All your processed bookmarks are displayed here with their respective categories and summaries.
764
 
765
- 2. **βœ… Select Bookmarks:**
766
- - Use the checkboxes next to each bookmark to select one, multiple, or all bookmarks you wish to manage.
767
 
768
- 3. **πŸ—‘οΈ Delete Selected Bookmarks:**
769
- - After selecting the desired bookmarks, click the **"Delete Selected Bookmarks"** button to remove them from your list.
770
 
771
- 4. **✏️ Edit Categories:**
772
- - Select the bookmarks you want to re-categorize.
773
- - Choose a new category from the dropdown menu labeled **"New Category"**.
774
- - Click the **"Edit Category of Selected Bookmarks"** button to update their categories.
775
 
776
- 5. **πŸ’Ύ Export Bookmarks:**
777
- - Click the **"Export Bookmarks"** button to download your updated bookmarks as an HTML file.
778
- - This file can be uploaded back to your browser to reflect the changes made within SmartMarks.
779
- """)
780
 
781
- manage_output = gr.Textbox(label="πŸ”„ Manage Output", interactive=False)
782
- bookmark_display_manage = gr.HTML(label="πŸ“„ Manage Bookmarks Display")
783
- bookmark_selector = gr.CheckboxGroup(label="βœ… Select Bookmarks", choices=[])
784
 
 
785
  new_category_input = gr.Dropdown(label="πŸ†• New Category", choices=CATEGORIES, value="Uncategorized")
786
  with gr.Row():
787
  delete_button = gr.Button("πŸ—‘οΈ Delete Selected Bookmarks")
788
  edit_category_button = gr.Button("✏️ Edit Category of Selected Bookmarks")
789
  export_button = gr.Button("πŸ’Ύ Export Bookmarks")
790
  download_link = gr.HTML(label="πŸ“₯ Download Exported Bookmarks")
 
791
 
792
  # Define button actions
793
  delete_button.click(
794
  delete_selected_bookmarks,
795
- inputs=bookmark_selector,
796
- outputs=[manage_output, bookmark_selector, bookmark_display_manage]
797
  )
798
 
799
  edit_category_button.click(
800
  edit_selected_bookmarks_category,
801
- inputs=[bookmark_selector, new_category_input],
802
- outputs=[manage_output, bookmark_selector, bookmark_display_manage]
803
  )
804
 
805
  export_button.click(
806
  export_bookmarks,
807
- inputs=None,
808
  outputs=download_link
809
  )
810
 
811
- # Initialize display after processing bookmarks
812
- process_button.click(
813
- process_uploaded_file,
814
- inputs=upload,
815
- outputs=[output_text, bookmark_display, bookmark_selector, bookmark_display_manage]
 
 
 
 
816
  )
817
 
818
  logger.info("Launching Gradio app")
 
2
 
3
  import gradio as gr
4
  from bs4 import BeautifulSoup
 
5
  from sentence_transformers import SentenceTransformer
6
  import faiss
7
  import numpy as np
 
12
  import logging
13
  import os
14
  import sys
15
+ import uuid # For unique IDs
16
 
17
  # Import OpenAI library
18
  import openai
 
35
  # Initialize models and variables
36
  logger.info("Initializing models and variables")
37
  embedding_model = SentenceTransformer('all-MiniLM-L6-v2')
38
+ faiss_index = faiss.IndexIDMap(faiss.IndexFlatL2(embedding_model.get_sentence_embedding_dimension()))
39
  bookmarks = []
40
  fetch_cache = {}
41
 
 
101
 
102
  try:
103
  logger.info(f"Fetching URL info for: {url}")
104
+ async with session.get(url, timeout=10) as response:
105
  bookmark['etag'] = response.headers.get('ETag', 'N/A')
106
  bookmark['status_code'] = response.status
107
 
 
122
  elif meta_description and meta_description.get('content'):
123
  description = meta_description.get('content')
124
  else:
125
+ # If no description, extract visible text
126
+ texts = soup.stripped_strings
127
+ description = ' '.join(texts[:200]) # Limit to first 200 words
128
+ # Generate summary using LLM
129
+ description = generate_summary_with_llm(description)
130
 
131
  bookmark['description'] = description
132
  logger.info(f"Fetched description for {url}")
 
146
  return bookmark
147
 
148
  # Asynchronous processing of bookmarks
149
+ async def process_bookmarks_async(bookmarks_list):
150
  logger.info("Processing bookmarks asynchronously")
151
  try:
152
  async with aiohttp.ClientSession() as session:
153
  tasks = []
154
+ for bookmark in bookmarks_list:
155
  task = asyncio.ensure_future(fetch_url_info(session, bookmark))
156
  tasks.append(task)
157
  await asyncio.gather(*tasks)
 
160
  logger.error(f"Error in asynchronous processing of bookmarks: {e}")
161
  raise
162
 
163
+ # Generate summary for a bookmark using LLM
164
+ def generate_summary_with_llm(text):
165
+ logger.info("Generating summary with LLM")
166
+ try:
167
+ prompt = f"Summarize the following content in a concise manner:\n\n{text}"
168
+ response = openai.ChatCompletion.create(
169
+ model='gpt-3.5-turbo', # Use appropriate model
170
+ messages=[
171
+ {"role": "system", "content": "You are a helpful assistant that summarizes text."},
172
+ {"role": "user", "content": prompt}
173
+ ],
174
+ max_tokens=150,
175
+ temperature=0.5,
176
+ )
177
+ summary = response['choices'][0]['message']['content'].strip()
178
+ logger.info("Summary generated successfully")
179
+ return summary
180
+ except Exception as e:
181
+ logger.error(f"Error generating summary with LLM: {e}")
182
+ return "No summary available."
183
+
184
  # Generate summary for a bookmark
185
  def generate_summary(bookmark):
186
  description = bookmark.get('description', '')
187
  if description:
188
  bookmark['summary'] = description
189
  else:
190
+ # Fallback summary generation
191
  title = bookmark.get('title', '')
192
  if title:
193
  bookmark['summary'] = title
 
196
  logger.info(f"Generated summary for bookmark: {bookmark.get('url')}")
197
  return bookmark
198
 
199
+ # Assign category to a bookmark using LLM
200
+ def assign_category_with_llm(summary):
201
+ logger.info("Assigning category with LLM")
202
+ try:
203
+ categories_str = ', '.join(CATEGORIES)
204
+ prompt = f"Assign the most appropriate category to the following summary from the list of categories.\n\nSummary: {summary}\n\nCategories: {categories_str}\n\nCategory:"
205
+ response = openai.ChatCompletion.create(
206
+ model='gpt-3.5-turbo', # Use appropriate model
207
+ messages=[
208
+ {"role": "system", "content": "You are a helpful assistant that assigns categories."},
209
+ {"role": "user", "content": prompt}
210
+ ],
211
+ max_tokens=10,
212
+ temperature=0.3,
213
+ )
214
+ category = response['choices'][0]['message']['content'].strip()
215
+ # Ensure the category is valid
216
+ if category in CATEGORIES:
217
+ logger.info(f"Assigned category '{category}' successfully")
218
+ return category
219
+ else:
220
+ logger.warning(f"Received invalid category '{category}' from LLM. Defaulting to 'Uncategorized'.")
221
+ return "Uncategorized"
222
+ except Exception as e:
223
+ logger.error(f"Error assigning category with LLM: {e}")
224
+ return "Uncategorized"
225
+
226
  # Assign category to a bookmark
227
  def assign_category(bookmark):
228
  if bookmark.get('dead_link'):
 
230
  logger.info(f"Assigned category 'Dead Link' to bookmark: {bookmark.get('url')}")
231
  return bookmark
232
 
233
+ summary = bookmark.get('summary', '')
234
+ if summary:
235
+ category = assign_category_with_llm(summary)
236
+ bookmark['category'] = category
237
+ else:
238
+ bookmark['category'] = 'Uncategorized'
239
+ logger.info(f"No summary available to assign category for bookmark: {bookmark.get('url')}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
240
  return bookmark
241
 
242
  # Vectorize summaries and build FAISS index
243
+ def vectorize_and_index(bookmarks_list):
244
+ global faiss_index
245
+ logger.info("Vectorizing summaries and updating FAISS index")
246
  try:
247
+ summaries = [bookmark['summary'] for bookmark in bookmarks_list]
248
+ embeddings = embedding_model.encode(summaries).astype('float32')
249
+ ids = [uuid.uuid4().int & (1<<64)-1 for _ in bookmarks_list] # Generate unique 64-bit integer IDs
250
+ faiss_index.add_with_ids(embeddings, np.array(ids))
251
+ logger.info("FAISS index updated successfully")
252
+ return embeddings, ids
 
253
  except Exception as e:
254
  logger.error(f"Error in vectorizing and indexing: {e}")
255
  raise
256
 
257
+ # Remove vectors from FAISS index by IDs
258
+ def remove_from_faiss(ids_to_remove):
259
+ global faiss_index
260
+ logger.info(f"Removing {len(ids_to_remove)} vectors from FAISS index")
261
+ try:
262
+ faiss_index.remove_ids(np.array(ids_to_remove))
263
+ logger.info("Vectors removed from FAISS index successfully")
264
+ except Exception as e:
265
+ logger.error(f"Error removing vectors from FAISS index: {e}")
266
+
267
  # Generate HTML display for bookmarks
268
  def display_bookmarks():
269
  logger.info("Generating HTML display for bookmarks")
 
301
  return cards
302
 
303
  # Process the uploaded file
304
+ def process_uploaded_file(file, state_bookmarks):
305
  global bookmarks, faiss_index
306
  logger.info("Processing uploaded file")
307
  if file is None:
308
  logger.warning("No file uploaded")
309
+ return (
310
+ "⚠️ Please upload a bookmarks HTML file.",
311
+ "",
312
+ gr.update(choices=[]),
313
+ display_bookmarks(),
314
+ state_bookmarks # Return the unchanged state
315
+ )
316
+
317
  try:
318
  file_content = file.decode('utf-8')
319
  except UnicodeDecodeError as e:
320
  logger.error(f"Error decoding the file: {e}")
321
+ return (
322
+ "⚠️ Error decoding the file. Please ensure it's a valid HTML file.",
323
+ "",
324
+ gr.update(choices=[]),
325
+ display_bookmarks(),
326
+ state_bookmarks # Return the unchanged state
327
+ )
328
 
329
  try:
330
  bookmarks = parse_bookmarks(file_content)
331
  except Exception as e:
332
  logger.error(f"Error parsing bookmarks: {e}")
333
+ return (
334
+ "⚠️ Error parsing the bookmarks HTML file.",
335
+ "",
336
+ gr.update(choices=[]),
337
+ display_bookmarks(),
338
+ state_bookmarks # Return the unchanged state
339
+ )
340
 
341
  if not bookmarks:
342
  logger.warning("No bookmarks found in the uploaded file")
343
+ return (
344
+ "⚠️ No bookmarks found in the uploaded file.",
345
+ "",
346
+ gr.update(choices=[]),
347
+ display_bookmarks(),
348
+ state_bookmarks # Return the unchanged state
349
+ )
350
 
351
  # Asynchronously fetch bookmark info
352
  try:
353
  asyncio.run(process_bookmarks_async(bookmarks))
354
  except Exception as e:
355
  logger.error(f"Error processing bookmarks asynchronously: {e}")
356
+ return (
357
+ "⚠️ Error processing bookmarks.",
358
+ "",
359
+ gr.update(choices=[]),
360
+ display_bookmarks(),
361
+ state_bookmarks # Return the unchanged state
362
+ )
363
 
364
  # Generate summaries and assign categories
365
  for bookmark in bookmarks:
 
367
  assign_category(bookmark)
368
 
369
  try:
370
+ embeddings, ids = vectorize_and_index(bookmarks)
371
  except Exception as e:
372
  logger.error(f"Error building FAISS index: {e}")
373
+ return (
374
+ "⚠️ Error building search index.",
375
+ "",
376
+ gr.update(choices=[]),
377
+ display_bookmarks(),
378
+ state_bookmarks # Return the unchanged state
379
+ )
380
+
381
+ # Assign unique IDs to bookmarks
382
+ for bookmark, id_ in zip(bookmarks, ids):
383
+ bookmark['id'] = id_
384
 
385
  message = f"βœ… Successfully processed {len(bookmarks)} bookmarks."
386
  logger.info(message)
387
  bookmark_html = display_bookmarks()
388
 
389
+ # Prepare Manage Bookmarks tab outputs
390
  choices = [f"{i+1}. {bookmark['title']} (Category: {bookmark['category']})" for i, bookmark in enumerate(bookmarks)]
391
+ bookmarks_html_manage = display_bookmarks()
392
 
393
+ # Update the shared state
394
+ updated_state = bookmarks.copy()
395
 
396
+ return (
397
+ message,
398
+ bookmark_html,
399
+ gr.update(choices=choices, value=[]),
400
+ bookmarks_html_manage,
401
+ updated_state # Return the updated state
402
+ )
403
 
404
  # Delete selected bookmarks
405
+ def delete_selected_bookmarks(selected_indices, state_bookmarks):
 
406
  if not selected_indices:
407
+ return "⚠️ No bookmarks selected.", gr.update(choices=[]), display_bookmarks(), state_bookmarks
408
+
409
+ bookmarks = state_bookmarks.copy()
410
+ ids_to_remove = []
411
+ indices = []
412
+ for s in selected_indices:
413
+ try:
414
+ idx = int(s.split('.')[0]) - 1
415
+ if 0 <= idx < len(bookmarks):
416
+ ids_to_remove.append(bookmarks[idx]['id'])
417
+ indices.append(idx)
418
+ else:
419
+ logger.warning(f"Index out of range: {idx + 1}")
420
+ except ValueError:
421
+ logger.error(f"Invalid selection format: {s}")
422
+
423
+ # Remove bookmarks from the list
424
  indices = sorted(indices, reverse=True)
425
  for idx in indices:
426
+ logger.info(f"Deleting bookmark at index {idx + 1}")
427
+ bookmarks.pop(idx)
428
+
429
+ # Remove embeddings from FAISS index
430
+ remove_from_faiss(ids_to_remove)
431
+
 
432
  message = "πŸ—‘οΈ Selected bookmarks deleted successfully."
433
  logger.info(message)
434
+
435
+ # Regenerate HTML display
 
 
436
  bookmarks_html = display_bookmarks()
437
+
438
+ # Update choices for selection
439
+ choices = [f"{i+1}. {bookmark['title']} (Category: {bookmark['category']})" for i, bookmark in enumerate(bookmarks)]
440
+
441
+ # Update the shared state
442
+ updated_state = bookmarks.copy()
443
+
444
+ return message, gr.update(choices=choices, value=[]), bookmarks_html, updated_state
445
 
446
  # Edit category of selected bookmarks
447
+ def edit_selected_bookmarks_category(selected_indices, new_category, state_bookmarks):
448
  if not selected_indices:
449
+ return (
450
+ "⚠️ No bookmarks selected.",
451
+ gr.update(choices=[f"{i+1}. {bookmark['title']} (Category: {bookmark['category']})" for i, bookmark in enumerate(state_bookmarks)], value=[]),
452
+ display_bookmarks(),
453
+ state_bookmarks
454
+ )
455
+
456
  if not new_category:
457
+ return (
458
+ "⚠️ No new category selected.",
459
+ gr.update(choices=[f"{i+1}. {bookmark['title']} (Category: {bookmark['category']})" for i, bookmark in enumerate(state_bookmarks)], value=[]),
460
+ display_bookmarks(),
461
+ state_bookmarks
462
+ )
463
+
464
+ bookmarks = state_bookmarks.copy()
465
+ for s in selected_indices:
466
+ try:
467
+ idx = int(s.split('.')[0]) - 1
468
+ if 0 <= idx < len(bookmarks):
469
+ bookmarks[idx]['category'] = new_category
470
+ logger.info(f"Updated category for bookmark {idx + 1} to {new_category}")
471
+ except ValueError:
472
+ logger.error(f"Invalid selection format: {s}")
473
+
474
  message = "✏️ Category updated for selected bookmarks."
475
  logger.info(message)
476
+
477
+ # Regenerate HTML display
 
 
478
  bookmarks_html = display_bookmarks()
479
+
480
+ # Update choices for selection
481
+ choices = [f"{i+1}. {bookmark['title']} (Category: {bookmark['category']})" for i, bookmark in enumerate(bookmarks)]
482
+
483
+ # Update the shared state
484
+ updated_state = bookmarks.copy()
485
+
486
+ return message, gr.update(choices=choices, value=[]), bookmarks_html, updated_state
487
 
488
  # Export bookmarks to HTML
489
+ def export_bookmarks(state_bookmarks):
490
+ bookmarks = state_bookmarks
491
  if not bookmarks:
492
  logger.warning("No bookmarks to export")
493
  return "⚠️ No bookmarks to export."
 
513
  logger.error(f"Error exporting bookmarks: {e}")
514
  return "⚠️ Error exporting bookmarks."
515
 
516
+ # Chatbot response using Groq Cloud API with FAISS search
517
+ def chatbot_response(user_query, state_bookmarks):
518
  if not GROQ_API_KEY:
519
  logger.warning("GROQ_API_KEY not set.")
520
  return "⚠️ API key not set. Please set the GROQ_API_KEY environment variable in the Hugging Face Space settings."
521
 
522
+ bookmarks = state_bookmarks
523
  if not bookmarks:
524
  logger.warning("No bookmarks available for chatbot")
525
  return "⚠️ No bookmarks available. Please upload and process your bookmarks first."
526
 
527
  logger.info(f"Chatbot received query: {user_query}")
528
 
529
+ # Prepare the prompt for the LLM using FAISS search
530
  try:
531
+ # Encode the user query
532
+ query_embedding = embedding_model.encode([user_query]).astype('float32')
533
+
534
+ # Perform FAISS search
535
+ k = 5 # Number of top results to retrieve
536
+ distances, indices = faiss_index.search(query_embedding, k)
537
+
538
+ # Fetch the corresponding bookmarks
539
+ relevant_bookmarks = []
540
+ for idx in indices[0]:
541
+ if idx == -1:
542
+ continue # No more results
543
+ # Find the bookmark with matching ID
544
+ for bookmark in bookmarks:
545
+ if bookmark.get('id') == idx:
546
+ relevant_bookmarks.append(bookmark)
547
+ break
548
+
549
+ if not relevant_bookmarks:
550
+ return "πŸ” No relevant bookmarks found for your query."
551
+
552
+ # Prepare the summary of relevant bookmarks
553
  bookmark_data = ""
554
+ for bm in relevant_bookmarks:
555
+ bookmark_data += f"Title: {bm['title']}\nURL: {bm['url']}\nSummary: {bm['summary']}\n\n"
556
 
557
  # Construct the prompt
558
  prompt = f"""
 
561
  User Query:
562
  {user_query}
563
 
564
+ Relevant Bookmarks:
565
  {bookmark_data}
566
 
567
+ Please provide a concise summary of the most relevant bookmarks that match the user's query.
568
  """
569
 
570
  # Call the Groq Cloud API via the OpenAI client
571
  response = openai.ChatCompletion.create(
572
+ model='gpt-3.5-turbo', # Use appropriate model
573
  messages=[
574
  {"role": "system", "content": "You help users find relevant bookmarks based on their queries."},
575
  {"role": "user", "content": prompt}
 
593
  def build_app():
594
  try:
595
  logger.info("Building Gradio app")
596
+ with gr.Blocks(css="""
597
+ .card {
598
+ box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
599
+ transition: 0.3s;
600
+ }
601
+ .card:hover {
602
+ box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
603
+ }
604
+
605
+ /* Dynamic Theme Styling */
606
+ @media (prefers-color-scheme: dark) {
607
+ body {
608
+ color: white;
609
+ background-color: #121212;
610
+ }
611
+ .card {
612
+ background-color: #1e1e1e;
613
+ }
614
+ a {
615
+ color: #bb86fc;
616
+ }
617
+ h1, h2, h3, p, strong {
618
+ color: inherit;
619
+ }
620
+ }
621
+
622
+ @media (prefers-color-scheme: light) {
623
+ body {
624
+ color: black;
625
+ background-color: white;
626
+ }
627
+ .card {
628
+ background-color: #fff;
629
+ }
630
+ a {
631
+ color: #1a0dab;
632
+ }
633
+ h1, h2, h3, p, strong {
634
+ color: inherit;
635
+ }
636
+ }
637
+ """) as demo:
638
+ # Shared states
639
+ state_bookmarks = gr.State([])
640
+ chat_history = gr.State([])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
641
 
642
  # General Overview
643
  gr.Markdown("""
644
+ # πŸ“š SmartMarks - AI Browser Bookmarks Manager
645
+
646
+ Welcome to **SmartMarks**, your intelligent assistant for managing browser bookmarks. SmartMarks leverages AI to help you organize, search, and interact with your bookmarks seamlessly. Whether you're looking to categorize your links, retrieve information quickly, or maintain an updated list, SmartMarks has you covered.
647
 
648
+ ---
649
 
650
+ ## πŸš€ **How to Use SmartMarks**
651
 
652
+ SmartMarks is divided into three main sections:
653
 
654
+ 1. **πŸ“‚ Upload and Process Bookmarks:** Import your existing bookmarks and let SmartMarks analyze and categorize them for you.
655
+ 2. **πŸ’¬ Chat with Bookmarks:** Interact with your bookmarks using natural language queries to find relevant links effortlessly.
656
+ 3. **πŸ› οΈ Manage Bookmarks:** View, edit, delete, and export your bookmarks with ease.
657
 
658
+ Navigate through the tabs to explore each feature in detail.
659
+ """)
 
660
 
661
+ # Define Manage Bookmarks components outside the tab for global access
662
+ bookmark_selector = gr.CheckboxGroup(label="βœ… Select Bookmarks", choices=[])
663
+ bookmark_display_manage = gr.HTML(label="πŸ“„ Manage Bookmarks Display")
664
 
665
  # Upload and Process Bookmarks Tab
666
  with gr.Tab("Upload and Process Bookmarks"):
667
  gr.Markdown("""
668
+ ## πŸ“‚ **Upload and Process Bookmarks**
669
 
670
+ ### πŸ“ **Steps to Upload and Process:**
671
 
672
+ 1. **πŸ”½ Upload Bookmarks File:**
673
+ - Click on the **"πŸ“ Upload Bookmarks HTML File"** button.
674
+ - Select your browser's exported bookmarks HTML file from your device.
675
 
676
+ 2. **βš™οΈ Process Bookmarks:**
677
+ - After uploading, click on the **"βš™οΈ Process Bookmarks"** button.
678
+ - SmartMarks will parse your bookmarks, fetch additional information, generate summaries, and categorize each link based on predefined categories.
679
 
680
+ 3. **πŸ“„ View Processed Bookmarks:**
681
+ - Once processing is complete, your bookmarks will be displayed in an organized and visually appealing format below.
682
+ """)
683
 
684
  upload = gr.File(label="πŸ“ Upload Bookmarks HTML File", type='binary')
685
  process_button = gr.Button("βš™οΈ Process Bookmarks")
686
  output_text = gr.Textbox(label="βœ… Output", interactive=False)
687
  bookmark_display = gr.HTML(label="πŸ“„ Bookmarks")
688
 
 
 
 
 
689
  process_button.click(
690
  process_uploaded_file,
691
+ inputs=[upload, state_bookmarks],
692
+ outputs=[output_text, bookmark_display, bookmark_selector, bookmark_display_manage, state_bookmarks]
693
  )
694
 
695
  # Chat with Bookmarks Tab
696
  with gr.Tab("Chat with Bookmarks"):
697
  gr.Markdown("""
698
+ ## πŸ’¬ **Chat with Bookmarks**
699
+
700
+ ### πŸ€– **How to Interact:**
701
 
702
+ 1. **✍️ Enter Your Query:**
703
+ - In the **"✍️ Ask about your bookmarks"** textbox, type your question or keyword related to your bookmarks. For example, "Do I have any bookmarks about GenerativeAI?"
704
 
705
+ 2. **πŸ“¨ Submit Your Query:**
706
+ - Click the **"πŸ“¨ Send"** button to submit your query.
707
 
708
+ 3. **πŸ“ˆ Receive AI-Driven Responses:**
709
+ - SmartMarks will analyze your query and provide relevant bookmarks that match your request, making it easier to find specific links without manual searching.
710
 
711
+ 4. **πŸ—‚οΈ View Chat History:**
712
+ - All your queries and the corresponding AI responses are displayed in the chat history for your reference.
713
+ """)
714
 
715
+ with gr.Row():
716
+ chat_history_display = gr.Chatbot(label="πŸ—¨οΈ Chat History")
717
+ with gr.Column(scale=1):
718
+ chat_input = gr.Textbox(
719
+ label="✍️ Ask about your bookmarks",
720
+ placeholder="e.g., Do I have any bookmarks about GenerativeAI?",
721
+ lines=1,
722
+ interactive=True
723
+ )
724
+ chat_button = gr.Button("πŸ“¨ Send")
725
+
726
+ # When user presses Enter in chat_input
727
+ chat_input.submit(
728
+ chatbot_response,
729
+ inputs=[chat_input, state_bookmarks],
730
+ outputs=chat_history_display
731
+ )
732
 
733
+ # When user clicks Send button
734
  chat_button.click(
735
  chatbot_response,
736
+ inputs=[chat_input, state_bookmarks],
737
+ outputs=chat_history_display
738
  )
739
 
740
  # Manage Bookmarks Tab
741
  with gr.Tab("Manage Bookmarks"):
742
  gr.Markdown("""
743
+ ## πŸ› οΈ **Manage Bookmarks**
744
 
745
+ ### πŸ—‚οΈ **Features:**
746
 
747
+ 1. **πŸ‘οΈ View Bookmarks:**
748
+ - All your processed bookmarks are displayed here with their respective categories and summaries.
749
 
750
+ 2. **βœ… Select Bookmarks:**
751
+ - Use the checkboxes next to each bookmark to select one, multiple, or all bookmarks you wish to manage.
752
 
753
+ 3. **πŸ—‘οΈ Delete Selected Bookmarks:**
754
+ - After selecting the desired bookmarks, click the **"πŸ—‘οΈ Delete Selected Bookmarks"** button to remove them from your list.
755
 
756
+ 4. **✏️ Edit Categories:**
757
+ - Select the bookmarks you want to re-categorize.
758
+ - Choose a new category from the dropdown menu labeled **"πŸ†• New Category"**.
759
+ - Click the **"✏️ Edit Category of Selected Bookmarks"** button to update their categories.
760
 
761
+ 5. **πŸ’Ύ Export Bookmarks:**
762
+ - Click the **"πŸ’Ύ Export Bookmarks"** button to download your updated bookmarks as an HTML file.
763
+ - This file can be uploaded back to your browser to reflect the changes made within SmartMarks.
 
764
 
765
+ 6. **πŸ”„ Refresh Bookmarks:**
766
+ - Click the **"πŸ”„ Refresh Bookmarks"** button to ensure the latest state is reflected in the display.
767
+ """)
768
 
769
+ manage_output = gr.Textbox(label="πŸ”„ Manage Output", interactive=False)
770
  new_category_input = gr.Dropdown(label="πŸ†• New Category", choices=CATEGORIES, value="Uncategorized")
771
  with gr.Row():
772
  delete_button = gr.Button("πŸ—‘οΈ Delete Selected Bookmarks")
773
  edit_category_button = gr.Button("✏️ Edit Category of Selected Bookmarks")
774
  export_button = gr.Button("πŸ’Ύ Export Bookmarks")
775
  download_link = gr.HTML(label="πŸ“₯ Download Exported Bookmarks")
776
+ refresh_button = gr.Button("πŸ”„ Refresh Bookmarks")
777
 
778
  # Define button actions
779
  delete_button.click(
780
  delete_selected_bookmarks,
781
+ inputs=[bookmark_selector, state_bookmarks],
782
+ outputs=[manage_output, bookmark_selector, bookmark_display_manage, state_bookmarks]
783
  )
784
 
785
  edit_category_button.click(
786
  edit_selected_bookmarks_category,
787
+ inputs=[bookmark_selector, new_category_input, state_bookmarks],
788
+ outputs=[manage_output, bookmark_selector, bookmark_display_manage, state_bookmarks]
789
  )
790
 
791
  export_button.click(
792
  export_bookmarks,
793
+ inputs=[state_bookmarks],
794
  outputs=download_link
795
  )
796
 
797
+ refresh_button.click(
798
+ lambda bookmarks: (
799
+ [
800
+ f"{i+1}. {bookmark['title']} (Category: {bookmark['category']})" for i, bookmark in enumerate(bookmarks)
801
+ ],
802
+ display_bookmarks()
803
+ ),
804
+ inputs=[state_bookmarks],
805
+ outputs=[bookmark_selector, bookmark_display_manage]
806
  )
807
 
808
  logger.info("Launching Gradio app")