Spaces:
Running
Running
siddhartharya
commited on
Update app.py
Browse files
app.py
CHANGED
@@ -159,7 +159,7 @@ def get_page_metadata(soup):
|
|
159 |
|
160 |
async def generate_summary_async(bookmark):
|
161 |
async with llm_semaphore:
|
162 |
-
generate_summary
|
163 |
|
164 |
def generate_summary(bookmark):
|
165 |
"""
|
@@ -270,7 +270,7 @@ Be concise and objective.
|
|
270 |
|
271 |
async def assign_category_async(bookmark):
|
272 |
async with llm_semaphore:
|
273 |
-
assign_category
|
274 |
|
275 |
def assign_category(bookmark):
|
276 |
"""
|
@@ -452,6 +452,10 @@ async def process_bookmarks_llm(bookmarks_list):
|
|
452 |
tasks = []
|
453 |
for bookmark in bookmarks_list:
|
454 |
tasks.append(generate_summary_async(bookmark))
|
|
|
|
|
|
|
|
|
455 |
tasks.append(assign_category_async(bookmark))
|
456 |
await asyncio.gather(*tasks)
|
457 |
logger.info("Completed LLM processing of bookmarks")
|
@@ -585,9 +589,167 @@ def process_uploaded_file(file):
|
|
585 |
|
586 |
return message, bookmark_html, gr.update(choices=choices), bookmark_html
|
587 |
|
588 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
589 |
|
590 |
-
# Build and launch the Gradio app
|
591 |
def build_app():
|
592 |
"""
|
593 |
Build and launch the Gradio app.
|
|
|
159 |
|
160 |
async def generate_summary_async(bookmark):
|
161 |
async with llm_semaphore:
|
162 |
+
await asyncio.get_event_loop().run_in_executor(None, generate_summary, bookmark)
|
163 |
|
164 |
def generate_summary(bookmark):
|
165 |
"""
|
|
|
270 |
|
271 |
async def assign_category_async(bookmark):
|
272 |
async with llm_semaphore:
|
273 |
+
await asyncio.get_event_loop().run_in_executor(None, assign_category, bookmark)
|
274 |
|
275 |
def assign_category(bookmark):
|
276 |
"""
|
|
|
452 |
tasks = []
|
453 |
for bookmark in bookmarks_list:
|
454 |
tasks.append(generate_summary_async(bookmark))
|
455 |
+
await asyncio.gather(*tasks)
|
456 |
+
|
457 |
+
tasks = []
|
458 |
+
for bookmark in bookmarks_list:
|
459 |
tasks.append(assign_category_async(bookmark))
|
460 |
await asyncio.gather(*tasks)
|
461 |
logger.info("Completed LLM processing of bookmarks")
|
|
|
589 |
|
590 |
return message, bookmark_html, gr.update(choices=choices), bookmark_html
|
591 |
|
592 |
+
def delete_selected_bookmarks(selected_indices):
|
593 |
+
"""
|
594 |
+
Delete selected bookmarks and remove their vectors from the FAISS index.
|
595 |
+
"""
|
596 |
+
global bookmarks, faiss_index
|
597 |
+
if not selected_indices:
|
598 |
+
return "⚠️ No bookmarks selected.", gr.update(choices=[]), display_bookmarks()
|
599 |
+
|
600 |
+
ids_to_delete = []
|
601 |
+
indices_to_delete = []
|
602 |
+
for s in selected_indices:
|
603 |
+
idx = int(s.split('.')[0]) - 1
|
604 |
+
if 0 <= idx < len(bookmarks):
|
605 |
+
bookmark_id = bookmarks[idx]['id']
|
606 |
+
ids_to_delete.append(bookmark_id)
|
607 |
+
indices_to_delete.append(idx)
|
608 |
+
logger.info(f"Deleting bookmark at index {idx + 1}")
|
609 |
+
|
610 |
+
# Remove vectors from FAISS index
|
611 |
+
if faiss_index is not None and ids_to_delete:
|
612 |
+
faiss_index.remove_ids(np.array(ids_to_delete, dtype=np.int64))
|
613 |
+
|
614 |
+
# Remove bookmarks from the list (reverse order to avoid index shifting)
|
615 |
+
for idx in sorted(indices_to_delete, reverse=True):
|
616 |
+
bookmarks.pop(idx)
|
617 |
+
|
618 |
+
message = "🗑️ Selected bookmarks deleted successfully."
|
619 |
+
logger.info(message)
|
620 |
+
choices = [f"{i+1}. {bookmark['title']} (Category: {bookmark['category']})"
|
621 |
+
for i, bookmark in enumerate(bookmarks)]
|
622 |
+
|
623 |
+
return message, gr.update(choices=choices), display_bookmarks()
|
624 |
+
|
625 |
+
def edit_selected_bookmarks_category(selected_indices, new_category):
|
626 |
+
"""
|
627 |
+
Edit category of selected bookmarks.
|
628 |
+
"""
|
629 |
+
if not selected_indices:
|
630 |
+
return "⚠️ No bookmarks selected.", gr.update(choices=[]), display_bookmarks()
|
631 |
+
if not new_category:
|
632 |
+
return "⚠️ No new category selected.", gr.update(choices=[]), display_bookmarks()
|
633 |
+
|
634 |
+
indices = [int(s.split('.')[0])-1 for s in selected_indices]
|
635 |
+
for idx in indices:
|
636 |
+
if 0 <= idx < len(bookmarks):
|
637 |
+
bookmarks[idx]['category'] = new_category
|
638 |
+
logger.info(f"Updated category for bookmark {idx + 1} to {new_category}")
|
639 |
+
|
640 |
+
message = "✏️ Category updated for selected bookmarks."
|
641 |
+
logger.info(message)
|
642 |
+
|
643 |
+
# Update choices and display
|
644 |
+
choices = [f"{i+1}. {bookmark['title']} (Category: {bookmark['category']})"
|
645 |
+
for i, bookmark in enumerate(bookmarks)]
|
646 |
+
|
647 |
+
return message, gr.update(choices=choices), display_bookmarks()
|
648 |
+
|
649 |
+
def export_bookmarks():
|
650 |
+
"""
|
651 |
+
Export bookmarks to HTML file.
|
652 |
+
"""
|
653 |
+
if not bookmarks:
|
654 |
+
logger.warning("No bookmarks to export")
|
655 |
+
return "⚠️ No bookmarks to export."
|
656 |
+
|
657 |
+
try:
|
658 |
+
logger.info("Exporting bookmarks to HTML")
|
659 |
+
soup = BeautifulSoup("<!DOCTYPE NETSCAPE-Bookmark-file-1><Title>Bookmarks</Title><H1>Bookmarks</H1>", 'html.parser')
|
660 |
+
dl = soup.new_tag('DL')
|
661 |
+
for bookmark in bookmarks:
|
662 |
+
dt = soup.new_tag('DT')
|
663 |
+
a = soup.new_tag('A', href=bookmark['url'])
|
664 |
+
a.string = bookmark['title']
|
665 |
+
dt.append(a)
|
666 |
+
dl.append(dt)
|
667 |
+
soup.append(dl)
|
668 |
+
html_content = str(soup)
|
669 |
+
b64 = base64.b64encode(html_content.encode()).decode()
|
670 |
+
href = f'data:text/html;base64,{b64}'
|
671 |
+
logger.info("Bookmarks exported successfully")
|
672 |
+
return f'<a href="{href}" download="bookmarks.html">💾 Download Exported Bookmarks</a>'
|
673 |
+
except Exception as e:
|
674 |
+
logger.error(f"Error exporting bookmarks: {e}", exc_info=True)
|
675 |
+
return "⚠️ Error exporting bookmarks."
|
676 |
+
|
677 |
+
def chatbot_response(user_query):
|
678 |
+
"""
|
679 |
+
Generate chatbot response using the FAISS index and embeddings.
|
680 |
+
"""
|
681 |
+
if not bookmarks or faiss_index is None:
|
682 |
+
logger.warning("No bookmarks available for chatbot")
|
683 |
+
return "⚠️ No bookmarks available. Please upload and process your bookmarks first."
|
684 |
+
|
685 |
+
logger.info(f"Chatbot received query: {user_query}")
|
686 |
+
|
687 |
+
try:
|
688 |
+
# Encode the user query
|
689 |
+
query_vector = embedding_model.encode([user_query]).astype('float32')
|
690 |
+
|
691 |
+
# Search the FAISS index
|
692 |
+
k = 5 # Number of results to return
|
693 |
+
distances, ids = faiss_index.search(query_vector, k)
|
694 |
+
ids = ids.flatten()
|
695 |
+
|
696 |
+
# Retrieve the bookmarks
|
697 |
+
id_to_bookmark = {bookmark['id']: bookmark for bookmark in bookmarks}
|
698 |
+
matching_bookmarks = [id_to_bookmark.get(id) for id in ids if id in id_to_bookmark]
|
699 |
+
|
700 |
+
if not matching_bookmarks:
|
701 |
+
return "No relevant bookmarks found for your query."
|
702 |
+
|
703 |
+
# Format the response
|
704 |
+
bookmarks_info = "\n".join([
|
705 |
+
f"Title: {bookmark['title']}\nURL: {bookmark['url']}\nSummary: {bookmark['summary']}"
|
706 |
+
for bookmark in matching_bookmarks
|
707 |
+
])
|
708 |
+
|
709 |
+
# Use the LLM via Groq Cloud API to generate a response
|
710 |
+
prompt = f"""
|
711 |
+
A user asked: "{user_query}"
|
712 |
+
|
713 |
+
Based on the bookmarks below, provide a helpful answer to the user's query, referencing the relevant bookmarks.
|
714 |
+
|
715 |
+
Bookmarks:
|
716 |
+
{bookmarks_info}
|
717 |
+
|
718 |
+
Provide a concise and helpful response.
|
719 |
+
"""
|
720 |
+
|
721 |
+
retries = 0
|
722 |
+
max_retries = 5
|
723 |
+
while retries <= max_retries:
|
724 |
+
try:
|
725 |
+
response = openai.ChatCompletion.create(
|
726 |
+
model='llama-3.1-70b-versatile',
|
727 |
+
messages=[
|
728 |
+
{"role": "user", "content": prompt}
|
729 |
+
],
|
730 |
+
max_tokens=500,
|
731 |
+
temperature=0.7,
|
732 |
+
)
|
733 |
+
break # Exit loop if successful
|
734 |
+
except openai.error.RateLimitError as e:
|
735 |
+
retry_after = extract_retry_after(str(e)) or exponential_backoff(retries)
|
736 |
+
logger.warning(f"Rate limit exceeded. Retrying after {retry_after} seconds.")
|
737 |
+
time.sleep(retry_after)
|
738 |
+
retries += 1
|
739 |
+
except Exception as e:
|
740 |
+
error_message = f"⚠️ Error processing your query: {str(e)}"
|
741 |
+
logger.error(error_message, exc_info=True)
|
742 |
+
return error_message
|
743 |
+
|
744 |
+
answer = response['choices'][0]['message']['content'].strip()
|
745 |
+
logger.info("Chatbot response generated using Groq Cloud API")
|
746 |
+
return answer
|
747 |
+
|
748 |
+
except Exception as e:
|
749 |
+
error_message = f"⚠️ Error processing your query: {str(e)}"
|
750 |
+
logger.error(error_message, exc_info=True)
|
751 |
+
return error_message
|
752 |
|
|
|
753 |
def build_app():
|
754 |
"""
|
755 |
Build and launch the Gradio app.
|