siddhartharya commited on
Commit
18ec658
β€’
1 Parent(s): 817113d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +179 -345
app.py CHANGED
@@ -12,7 +12,6 @@ import base64
12
  import logging
13
  import os
14
  import sys
15
- import uuid # For unique IDs
16
 
17
  # Import OpenAI library
18
  import openai
@@ -35,7 +34,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 = faiss.IndexIDMap(faiss.IndexFlatL2(embedding_model.get_sentence_embedding_dimension()))
39
  bookmarks = []
40
  fetch_cache = {}
41
 
@@ -101,7 +100,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=10) as response:
105
  bookmark['etag'] = response.headers.get('ETag', 'N/A')
106
  bookmark['status_code'] = response.status
107
 
@@ -122,11 +121,7 @@ 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
- # 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,12 +141,12 @@ async def fetch_url_info(session, bookmark):
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,34 +155,12 @@ async def process_bookmarks_async(bookmarks_list):
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,33 +169,6 @@ def generate_summary(bookmark):
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,40 +176,62 @@ def assign_category(bookmark):
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,65 +269,34 @@ def display_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,127 +304,70 @@ def process_uploaded_file(file, state_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,46 +393,25 @@ def export_bookmarks(state_bookmarks):
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,15 +420,15 @@ You are an assistant that helps users find relevant bookmarks from their collect
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,175 +452,150 @@ Please provide a concise summary of the most relevant bookmarks that match the u
593
  def build_app():
594
  try:
595
  logger.info("Building Gradio app")
596
- with gr.Blocks(css="app.css") as demo: # Load external CSS file
597
- # Shared states
598
- state_bookmarks = gr.State([])
599
- chat_history = gr.State([])
600
-
601
  # General Overview
602
  gr.Markdown("""
603
- # πŸ“š SmartMarks - AI Browser Bookmarks Manager
604
 
605
- 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.
606
 
607
- ---
608
 
609
- ## πŸš€ **How to Use SmartMarks**
610
 
611
- SmartMarks is divided into three main sections:
612
 
613
- 1. **πŸ“‚ Upload and Process Bookmarks:** Import your existing bookmarks and let SmartMarks analyze and categorize them for you.
614
- 2. **πŸ’¬ Chat with Bookmarks:** Interact with your bookmarks using natural language queries to find relevant links effortlessly.
615
- 3. **πŸ› οΈ Manage Bookmarks:** View, edit, delete, and export your bookmarks with ease.
616
 
617
- Navigate through the tabs to explore each feature in detail.
618
- """)
619
-
620
- # Define Manage Bookmarks components outside the tab for global access
621
- bookmark_selector = gr.CheckboxGroup(label="βœ… Select Bookmarks", choices=[])
622
- bookmark_display_manage = gr.HTML(label="πŸ“„ Manage Bookmarks Display")
623
 
624
  # Upload and Process Bookmarks Tab
625
  with gr.Tab("Upload and Process Bookmarks"):
626
  gr.Markdown("""
627
- ## πŸ“‚ **Upload and Process Bookmarks**
628
 
629
- ### πŸ“ **Steps to Upload and Process:**
630
 
631
- 1. **πŸ”½ Upload Bookmarks File:**
632
- - Click on the **"πŸ“ Upload Bookmarks HTML File"** button.
633
- - Select your browser's exported bookmarks HTML file from your device.
634
 
635
- 2. **βš™οΈ Process Bookmarks:**
636
- - After uploading, click on the **"βš™οΈ Process Bookmarks"** button.
637
- - SmartMarks will parse your bookmarks, fetch additional information, generate summaries, and categorize each link based on predefined categories.
638
 
639
- 3. **πŸ“„ View Processed Bookmarks:**
640
- - Once processing is complete, your bookmarks will be displayed in an organized and visually appealing format below.
641
- """)
642
 
643
  upload = gr.File(label="πŸ“ Upload Bookmarks HTML File", type='binary')
644
  process_button = gr.Button("βš™οΈ Process Bookmarks")
645
  output_text = gr.Textbox(label="βœ… Output", interactive=False)
646
  bookmark_display = gr.HTML(label="πŸ“„ Bookmarks")
647
 
 
 
 
 
648
  process_button.click(
649
  process_uploaded_file,
650
- inputs=[upload, state_bookmarks],
651
- outputs=[output_text, bookmark_display, bookmark_selector, bookmark_display_manage, state_bookmarks]
652
  )
653
 
654
  # Chat with Bookmarks Tab
655
  with gr.Tab("Chat with Bookmarks"):
656
  gr.Markdown("""
657
- ## πŸ’¬ **Chat with Bookmarks**
658
 
659
- ### πŸ€– **How to Interact:**
660
 
661
- 1. **✍️ Enter Your Query:**
662
- - 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?"
663
 
664
- 2. **πŸ“¨ Submit Your Query:**
665
- - Click the **"πŸ“¨ Send"** button to submit your query.
666
 
667
- 3. **πŸ“ˆ Receive AI-Driven Responses:**
668
- - SmartMarks will analyze your query and provide relevant bookmarks that match your request, making it easier to find specific links without manual searching.
 
669
 
670
- 4. **πŸ—‚οΈ View Chat History:**
671
- - All your queries and the corresponding AI responses are displayed in the chat history for your reference.
672
- """)
673
 
674
- with gr.Row():
675
- chat_history_display = gr.Chatbot(label="πŸ—¨οΈ Chat History")
676
- with gr.Column(scale=1):
677
- chat_input = gr.Textbox(
678
- label="✍️ Ask about your bookmarks",
679
- placeholder="e.g., Do I have any bookmarks about GenerativeAI?",
680
- lines=1,
681
- interactive=True
682
- )
683
- chat_button = gr.Button("πŸ“¨ Send")
684
-
685
- # When user presses Enter in chat_input
686
- chat_input.submit(
687
- chatbot_response,
688
- inputs=[chat_input, state_bookmarks],
689
- outputs=chat_history_display
690
- )
691
-
692
- # When user clicks Send button
693
  chat_button.click(
694
  chatbot_response,
695
- inputs=[chat_input, state_bookmarks],
696
- outputs=chat_history_display
697
  )
698
 
699
  # Manage Bookmarks Tab
700
  with gr.Tab("Manage Bookmarks"):
701
  gr.Markdown("""
702
- ## πŸ› οΈ **Manage Bookmarks**
703
 
704
- ### πŸ—‚οΈ **Features:**
705
 
706
- 1. **πŸ‘οΈ View Bookmarks:**
707
- - All your processed bookmarks are displayed here with their respective categories and summaries.
708
 
709
- 2. **βœ… Select Bookmarks:**
710
- - Use the checkboxes next to each bookmark to select one, multiple, or all bookmarks you wish to manage.
711
 
712
- 3. **πŸ—‘οΈ Delete Selected Bookmarks:**
713
- - After selecting the desired bookmarks, click the **"πŸ—‘οΈ Delete Selected Bookmarks"** button to remove them from your list.
714
 
715
- 4. **✏️ Edit Categories:**
716
- - Select the bookmarks you want to re-categorize.
717
- - Choose a new category from the dropdown menu labeled **"πŸ†• New Category"**.
718
- - Click the **"✏️ Edit Category of Selected Bookmarks"** button to update their categories.
719
 
720
- 5. **πŸ’Ύ Export Bookmarks:**
721
- - Click the **"πŸ’Ύ Export Bookmarks"** button to download your updated bookmarks as an HTML file.
722
- - This file can be uploaded back to your browser to reflect the changes made within SmartMarks.
723
-
724
- 6. **πŸ”„ Refresh Bookmarks:**
725
- - Click the **"πŸ”„ Refresh Bookmarks"** button to ensure the latest state is reflected in the display.
726
- """)
727
 
728
  manage_output = gr.Textbox(label="πŸ”„ Manage Output", interactive=False)
 
 
 
729
  new_category_input = gr.Dropdown(label="πŸ†• New Category", choices=CATEGORIES, value="Uncategorized")
730
  with gr.Row():
731
  delete_button = gr.Button("πŸ—‘οΈ Delete Selected Bookmarks")
732
  edit_category_button = gr.Button("✏️ Edit Category of Selected Bookmarks")
733
  export_button = gr.Button("πŸ’Ύ Export Bookmarks")
734
  download_link = gr.HTML(label="πŸ“₯ Download Exported Bookmarks")
735
- refresh_button = gr.Button("πŸ”„ Refresh Bookmarks")
736
 
737
  # Define button actions
738
  delete_button.click(
739
  delete_selected_bookmarks,
740
- inputs=[bookmark_selector, state_bookmarks],
741
- outputs=[manage_output, bookmark_selector, bookmark_display_manage, state_bookmarks]
742
  )
743
 
744
  edit_category_button.click(
745
  edit_selected_bookmarks_category,
746
- inputs=[bookmark_selector, new_category_input, state_bookmarks],
747
- outputs=[manage_output, bookmark_selector, bookmark_display_manage, state_bookmarks]
748
  )
749
 
750
  export_button.click(
751
  export_bookmarks,
752
- inputs=[state_bookmarks],
753
  outputs=download_link
754
  )
755
 
756
- refresh_button.click(
757
- lambda bookmarks: (
758
- [
759
- f"{i+1}. {bookmark['title']} (Category: {bookmark['category']})" for i, bookmark in enumerate(bookmarks)
760
- ],
761
- display_bookmarks()
762
- ),
763
- inputs=[state_bookmarks],
764
- outputs=[bookmark_selector, bookmark_display_manage]
765
  )
766
 
767
  logger.info("Launching Gradio app")
 
12
  import logging
13
  import os
14
  import sys
 
15
 
16
  # Import OpenAI library
17
  import openai
 
34
  # Initialize models and variables
35
  logger.info("Initializing models and variables")
36
  embedding_model = SentenceTransformer('all-MiniLM-L6-v2')
37
+ faiss_index = None
38
  bookmarks = []
39
  fetch_cache = {}
40
 
 
100
 
101
  try:
102
  logger.info(f"Fetching URL info for: {url}")
103
+ async with session.get(url, timeout=5) as response:
104
  bookmark['etag'] = response.headers.get('ETag', 'N/A')
105
  bookmark['status_code'] = response.status
106
 
 
121
  elif meta_description and meta_description.get('content'):
122
  description = meta_description.get('content')
123
  else:
124
+ description = ''
 
 
 
 
125
 
126
  bookmark['description'] = description
127
  logger.info(f"Fetched description for {url}")
 
141
  return bookmark
142
 
143
  # Asynchronous processing of bookmarks
144
+ async def process_bookmarks_async(bookmarks):
145
  logger.info("Processing bookmarks asynchronously")
146
  try:
147
  async with aiohttp.ClientSession() as session:
148
  tasks = []
149
+ for bookmark in bookmarks:
150
  task = asyncio.ensure_future(fetch_url_info(session, bookmark))
151
  tasks.append(task)
152
  await asyncio.gather(*tasks)
 
155
  logger.error(f"Error in asynchronous processing of bookmarks: {e}")
156
  raise
157
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  # Generate summary for a bookmark
159
  def generate_summary(bookmark):
160
  description = bookmark.get('description', '')
161
  if description:
162
  bookmark['summary'] = description
163
  else:
 
164
  title = bookmark.get('title', '')
165
  if title:
166
  bookmark['summary'] = title
 
169
  logger.info(f"Generated summary for bookmark: {bookmark.get('url')}")
170
  return bookmark
171
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
  # Assign category to a bookmark
173
  def assign_category(bookmark):
174
  if bookmark.get('dead_link'):
 
176
  logger.info(f"Assigned category 'Dead Link' to bookmark: {bookmark.get('url')}")
177
  return bookmark
178
 
179
+ summary = bookmark.get('summary', '').lower()
180
+ assigned_category = 'Uncategorized'
181
+
182
+ # Keywords associated with each category
183
+ category_keywords = {
184
+ "Social Media": ["social media", "networking", "friends", "connect", "posts", "profile"],
185
+ "News and Media": ["news", "journalism", "media", "headlines", "breaking news"],
186
+ "Education and Learning": ["education", "learning", "courses", "tutorial", "university", "academy", "study"],
187
+ "Entertainment": ["entertainment", "movies", "tv shows", "games", "comics", "fun"],
188
+ "Shopping and E-commerce": ["shopping", "e-commerce", "buy", "sell", "marketplace", "deals", "store"],
189
+ "Finance and Banking": ["finance", "banking", "investment", "money", "economy", "stock", "trading"],
190
+ "Technology": ["technology", "tech", "gadgets", "software", "computers", "innovation"],
191
+ "Health and Fitness": ["health", "fitness", "medical", "wellness", "exercise", "diet"],
192
+ "Travel and Tourism": ["travel", "tourism", "destinations", "hotels", "flights", "vacation"],
193
+ "Food and Recipes": ["food", "recipes", "cooking", "cuisine", "restaurant", "dining"],
194
+ "Sports": ["sports", "scores", "teams", "athletics", "matches", "leagues"],
195
+ "Arts and Culture": ["arts", "culture", "museum", "gallery", "exhibition", "artistic"],
196
+ "Government and Politics": ["government", "politics", "policy", "election", "public service"],
197
+ "Business and Economy": ["business", "corporate", "industry", "economy", "markets"],
198
+ "Science and Research": ["science", "research", "experiment", "laboratory", "study", "scientific"],
199
+ "Personal Blogs and Journals": ["blog", "journal", "personal", "diary", "thoughts", "opinions"],
200
+ "Job Search and Careers": ["jobs", "careers", "recruitment", "resume", "employment", "hiring"],
201
+ "Music and Audio": ["music", "audio", "songs", "albums", "artists", "bands"],
202
+ "Videos and Movies": ["video", "movies", "film", "clips", "trailers", "cinema"],
203
+ "Reference and Knowledge Bases": ["reference", "encyclopedia", "dictionary", "wiki", "knowledge", "information"],
204
+ }
205
+
206
+ for category, keywords in category_keywords.items():
207
+ for keyword in keywords:
208
+ if re.search(r'\b' + re.escape(keyword) + r'\b', summary):
209
+ assigned_category = category
210
+ logger.info(f"Assigned category '{assigned_category}' to bookmark: {bookmark.get('url')}")
211
+ break
212
+ if assigned_category != 'Uncategorized':
213
+ break
214
+
215
+ bookmark['category'] = assigned_category
216
+ if assigned_category == 'Uncategorized':
217
+ logger.info(f"No matching category found for bookmark: {bookmark.get('url')}")
218
  return bookmark
219
 
220
  # Vectorize summaries and build FAISS index
221
+ def vectorize_and_index(bookmarks):
222
+ logger.info("Vectorizing summaries and building FAISS index")
 
223
  try:
224
+ summaries = [bookmark['summary'] for bookmark in bookmarks]
225
+ embeddings = embedding_model.encode(summaries)
226
+ dimension = embeddings.shape[1]
227
+ faiss_idx = faiss.IndexFlatL2(dimension)
228
+ faiss_idx.add(np.array(embeddings))
229
+ logger.info("FAISS index built successfully")
230
+ return faiss_idx, embeddings
231
  except Exception as e:
232
  logger.error(f"Error in vectorizing and indexing: {e}")
233
  raise
234
 
 
 
 
 
 
 
 
 
 
 
235
  # Generate HTML display for bookmarks
236
  def display_bookmarks():
237
  logger.info("Generating HTML display for bookmarks")
 
269
  return cards
270
 
271
  # Process the uploaded file
272
+ def process_uploaded_file(file):
273
  global bookmarks, faiss_index
274
  logger.info("Processing uploaded file")
275
  if file is None:
276
  logger.warning("No file uploaded")
277
+ return "Please upload a bookmarks HTML file.", '', gr.update(choices=[]), display_bookmarks()
 
 
 
 
 
 
 
278
  try:
279
  file_content = file.decode('utf-8')
280
  except UnicodeDecodeError as e:
281
  logger.error(f"Error decoding the file: {e}")
282
+ return "Error decoding the file. Please ensure it's a valid HTML file.", '', gr.update(choices=[]), display_bookmarks()
 
 
 
 
 
 
283
 
284
  try:
285
  bookmarks = parse_bookmarks(file_content)
286
  except Exception as e:
287
  logger.error(f"Error parsing bookmarks: {e}")
288
+ return "Error parsing the bookmarks HTML file.", '', gr.update(choices=[]), display_bookmarks()
 
 
 
 
 
 
289
 
290
  if not bookmarks:
291
  logger.warning("No bookmarks found in the uploaded file")
292
+ return "No bookmarks found in the uploaded file.", '', gr.update(choices=[]), display_bookmarks()
 
 
 
 
 
 
293
 
294
  # Asynchronously fetch bookmark info
295
  try:
296
  asyncio.run(process_bookmarks_async(bookmarks))
297
  except Exception as e:
298
  logger.error(f"Error processing bookmarks asynchronously: {e}")
299
+ return "Error processing bookmarks.", '', gr.update(choices=[]), display_bookmarks()
 
 
 
 
 
 
300
 
301
  # Generate summaries and assign categories
302
  for bookmark in bookmarks:
 
304
  assign_category(bookmark)
305
 
306
  try:
307
+ faiss_index, embeddings = vectorize_and_index(bookmarks)
308
  except Exception as e:
309
  logger.error(f"Error building FAISS index: {e}")
310
+ return "Error building search index.", '', gr.update(choices=[]), display_bookmarks()
 
 
 
 
 
 
 
 
 
 
311
 
312
  message = f"βœ… Successfully processed {len(bookmarks)} bookmarks."
313
  logger.info(message)
314
  bookmark_html = display_bookmarks()
315
 
316
+ # Update bookmark_selector choices
317
  choices = [f"{i+1}. {bookmark['title']} (Category: {bookmark['category']})" for i, bookmark in enumerate(bookmarks)]
318
+ bookmark_selector_update = gr.update(choices=choices, value=[])
319
 
320
+ # Update bookmark_display_manage
321
+ bookmark_display_manage_update = display_bookmarks()
322
 
323
+ return message, bookmark_html, bookmark_selector_update, bookmark_display_manage_update
 
 
 
 
 
 
324
 
325
  # Delete selected bookmarks
326
+ def delete_selected_bookmarks(selected_indices):
327
+ global bookmarks, faiss_index
328
  if not selected_indices:
329
+ return "⚠️ No bookmarks selected.", gr.update(choices=[]), display_bookmarks()
330
+ indices = [int(s.split('.')[0])-1 for s in selected_indices]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
331
  indices = sorted(indices, reverse=True)
332
  for idx in indices:
333
+ if 0 <= idx < len(bookmarks):
334
+ logger.info(f"Deleting bookmark at index {idx + 1}")
335
+ bookmarks.pop(idx)
336
+ if bookmarks:
337
+ faiss_index, embeddings = vectorize_and_index(bookmarks)
338
+ else:
339
+ faiss_index = None
340
  message = "πŸ—‘οΈ Selected bookmarks deleted successfully."
341
  logger.info(message)
342
+ # Update bookmark_selector choices
 
 
 
 
343
  choices = [f"{i+1}. {bookmark['title']} (Category: {bookmark['category']})" for i, bookmark in enumerate(bookmarks)]
344
+ bookmark_selector_update = gr.update(choices=choices, value=[])
345
+ # Update bookmarks display
346
+ bookmarks_html = display_bookmarks()
347
+ return message, bookmark_selector_update, bookmarks_html
 
348
 
349
  # Edit category of selected bookmarks
350
+ def edit_selected_bookmarks_category(selected_indices, new_category):
351
  if not selected_indices:
352
+ return "⚠️ No bookmarks selected.", '', gr.update()
 
 
 
 
 
 
353
  if not new_category:
354
+ return "⚠️ No new category selected.", '', gr.update()
355
+ indices = [int(s.split('.')[0])-1 for s in selected_indices]
356
+ for idx in indices:
357
+ if 0 <= idx < len(bookmarks):
358
+ bookmarks[idx]['category'] = new_category
359
+ logger.info(f"Updated category for bookmark {idx + 1} to {new_category}")
 
 
 
 
 
 
 
 
 
 
 
360
  message = "✏️ Category updated for selected bookmarks."
361
  logger.info(message)
362
+ # Update bookmark_selector choices
 
 
 
 
363
  choices = [f"{i+1}. {bookmark['title']} (Category: {bookmark['category']})" for i, bookmark in enumerate(bookmarks)]
364
+ bookmark_selector_update = gr.update(choices=choices, value=[])
365
+ # Update bookmarks display
366
+ bookmarks_html = display_bookmarks()
367
+ return message, bookmark_selector_update, bookmarks_html
 
368
 
369
  # Export bookmarks to HTML
370
+ def export_bookmarks():
 
371
  if not bookmarks:
372
  logger.warning("No bookmarks to export")
373
  return "⚠️ No bookmarks to export."
 
393
  logger.error(f"Error exporting bookmarks: {e}")
394
  return "⚠️ Error exporting bookmarks."
395
 
396
+ # Chatbot response using Groq Cloud API
397
+ def chatbot_response(user_query):
398
  if not GROQ_API_KEY:
399
  logger.warning("GROQ_API_KEY not set.")
400
  return "⚠️ API key not set. Please set the GROQ_API_KEY environment variable in the Hugging Face Space settings."
401
 
 
402
  if not bookmarks:
403
  logger.warning("No bookmarks available for chatbot")
404
  return "⚠️ No bookmarks available. Please upload and process your bookmarks first."
405
 
406
  logger.info(f"Chatbot received query: {user_query}")
407
 
408
+ # Prepare the prompt for the LLM
409
  try:
410
+ # Limit the number of bookmarks to prevent exceeding token limits
411
+ max_bookmarks = 50 # Adjust as needed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
412
  bookmark_data = ""
413
+ for idx, bookmark in enumerate(bookmarks[:max_bookmarks]):
414
+ bookmark_data += f"{idx+1}. Title: {bookmark['title']}\nURL: {bookmark['url']}\nSummary: {bookmark['summary']}\n\n"
415
 
416
  # Construct the prompt
417
  prompt = f"""
 
420
  User Query:
421
  {user_query}
422
 
423
+ Bookmarks:
424
  {bookmark_data}
425
 
426
+ 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.
427
  """
428
 
429
  # Call the Groq Cloud API via the OpenAI client
430
  response = openai.ChatCompletion.create(
431
+ model='llama3-8b-8192', # Verify this model name with Groq Cloud API documentation
432
  messages=[
433
  {"role": "system", "content": "You help users find relevant bookmarks based on their queries."},
434
  {"role": "user", "content": prompt}
 
452
  def build_app():
453
  try:
454
  logger.info("Building Gradio app")
455
+ with gr.Blocks() as demo:
456
+ # Embed the CSS and Theme Toggle Switch
457
+
 
 
458
  # General Overview
459
  gr.Markdown("""
460
+ # πŸ“š SmartMarks - AI Browser Bookmarks Manager
461
 
462
+ 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.
463
 
464
+ ---
465
 
466
+ ## πŸš€ **How to Use SmartMarks**
467
 
468
+ SmartMarks is divided into three main sections:
469
 
470
+ 1. **πŸ“‚ Upload and Process Bookmarks:** Import your existing bookmarks and let SmartMarks analyze and categorize them for you.
471
+ 2. **πŸ’¬ Chat with Bookmarks:** Interact with your bookmarks using natural language queries to find relevant links effortlessly.
472
+ 3. **πŸ› οΈ Manage Bookmarks:** View, edit, delete, and export your bookmarks with ease.
473
 
474
+ Navigate through the tabs to explore each feature in detail.
475
+ """)
 
 
 
 
476
 
477
  # Upload and Process Bookmarks Tab
478
  with gr.Tab("Upload and Process Bookmarks"):
479
  gr.Markdown("""
480
+ ## πŸ“‚ **Upload and Process Bookmarks**
481
 
482
+ ### πŸ“ **Steps to Upload and Process:**
483
 
484
+ 1. **πŸ”½ Upload Bookmarks File:**
485
+ - Click on the **"Upload Bookmarks HTML File"** button.
486
+ - Select your browser's exported bookmarks HTML file from your device.
487
 
488
+ 2. **βš™οΈ Process Bookmarks:**
489
+ - After uploading, click on the **"Process Bookmarks"** button.
490
+ - SmartMarks will parse your bookmarks, fetch additional information, generate summaries, and categorize each link based on predefined categories.
491
 
492
+ 3. **πŸ“„ View Processed Bookmarks:**
493
+ - Once processing is complete, your bookmarks will be displayed in an organized and visually appealing format below.
494
+ """)
495
 
496
  upload = gr.File(label="πŸ“ Upload Bookmarks HTML File", type='binary')
497
  process_button = gr.Button("βš™οΈ Process Bookmarks")
498
  output_text = gr.Textbox(label="βœ… Output", interactive=False)
499
  bookmark_display = gr.HTML(label="πŸ“„ Bookmarks")
500
 
501
+ # Initialize Manage Bookmarks components
502
+ bookmark_selector = gr.CheckboxGroup(label="βœ… Select Bookmarks", choices=[])
503
+ bookmark_display_manage = gr.HTML(label="πŸ“„ Manage Bookmarks Display")
504
+
505
  process_button.click(
506
  process_uploaded_file,
507
+ inputs=upload,
508
+ outputs=[output_text, bookmark_display, bookmark_selector, bookmark_display_manage]
509
  )
510
 
511
  # Chat with Bookmarks Tab
512
  with gr.Tab("Chat with Bookmarks"):
513
  gr.Markdown("""
514
+ ## πŸ’¬ **Chat with Bookmarks**
515
 
516
+ ### πŸ€– **How to Interact:**
517
 
518
+ 1. **✍️ Enter Your Query:**
519
+ - 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?"
520
 
521
+ 2. **πŸ“¨ Submit Your Query:**
522
+ - Click the **"Send"** button to submit your query.
523
 
524
+ 3. **πŸ“ˆ Receive AI-Driven Responses:**
525
+ - SmartMarks will analyze your query and provide relevant bookmarks that match your request, making it easier to find specific links without manual searching.
526
+ """)
527
 
528
+ user_input = gr.Textbox(label="✍️ Ask about your bookmarks", placeholder="e.g., Do I have any bookmarks about GenerativeAI?")
529
+ chat_output = gr.Textbox(label="πŸ’¬ Chatbot Response", interactive=False)
530
+ chat_button = gr.Button("πŸ“¨ Send")
531
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
532
  chat_button.click(
533
  chatbot_response,
534
+ inputs=user_input,
535
+ outputs=chat_output
536
  )
537
 
538
  # Manage Bookmarks Tab
539
  with gr.Tab("Manage Bookmarks"):
540
  gr.Markdown("""
541
+ ## πŸ› οΈ **Manage Bookmarks**
542
 
543
+ ### πŸ—‚οΈ **Features:**
544
 
545
+ 1. **πŸ‘οΈ View Bookmarks:**
546
+ - All your processed bookmarks are displayed here with their respective categories and summaries.
547
 
548
+ 2. **βœ… Select Bookmarks:**
549
+ - Use the checkboxes next to each bookmark to select one, multiple, or all bookmarks you wish to manage.
550
 
551
+ 3. **πŸ—‘οΈ Delete Selected Bookmarks:**
552
+ - After selecting the desired bookmarks, click the **"Delete Selected Bookmarks"** button to remove them from your list.
553
 
554
+ 4. **✏️ Edit Categories:**
555
+ - Select the bookmarks you want to re-categorize.
556
+ - Choose a new category from the dropdown menu labeled **"New Category"**.
557
+ - Click the **"Edit Category of Selected Bookmarks"** button to update their categories.
558
 
559
+ 5. **πŸ’Ύ Export Bookmarks:**
560
+ - Click the **"Export Bookmarks"** button to download your updated bookmarks as an HTML file.
561
+ - This file can be uploaded back to your browser to reflect the changes made within SmartMarks.
562
+ """)
 
 
 
563
 
564
  manage_output = gr.Textbox(label="πŸ”„ Manage Output", interactive=False)
565
+ bookmark_display_manage = gr.HTML(label="πŸ“„ Manage Bookmarks Display")
566
+ bookmark_selector = gr.CheckboxGroup(label="βœ… Select Bookmarks", choices=[])
567
+
568
  new_category_input = gr.Dropdown(label="πŸ†• New Category", choices=CATEGORIES, value="Uncategorized")
569
  with gr.Row():
570
  delete_button = gr.Button("πŸ—‘οΈ Delete Selected Bookmarks")
571
  edit_category_button = gr.Button("✏️ Edit Category of Selected Bookmarks")
572
  export_button = gr.Button("πŸ’Ύ Export Bookmarks")
573
  download_link = gr.HTML(label="πŸ“₯ Download Exported Bookmarks")
 
574
 
575
  # Define button actions
576
  delete_button.click(
577
  delete_selected_bookmarks,
578
+ inputs=bookmark_selector,
579
+ outputs=[manage_output, bookmark_selector, bookmark_display_manage]
580
  )
581
 
582
  edit_category_button.click(
583
  edit_selected_bookmarks_category,
584
+ inputs=[bookmark_selector, new_category_input],
585
+ outputs=[manage_output, bookmark_selector, bookmark_display_manage]
586
  )
587
 
588
  export_button.click(
589
  export_bookmarks,
590
+ inputs=None,
591
  outputs=download_link
592
  )
593
 
594
+ # Initialize display after processing bookmarks
595
+ process_button.click(
596
+ process_uploaded_file,
597
+ inputs=upload,
598
+ outputs=[output_text, bookmark_display, bookmark_selector, bookmark_display_manage]
 
 
 
 
599
  )
600
 
601
  logger.info("Launching Gradio app")