ReMind / src /agents /bookmarks_agent.py
GhostDragon01's picture
refactor: Reduce verbosity in agent outputs and enhance message formatting in chat interface
b58981e
from typing import List, Dict, Any
from smolagents import CodeAgent, InferenceClientModel, tool
import os
import json
import sys
from pathlib import Path
from datetime import datetime
def find_chrome_bookmarks_file():
"""
Automatically detects the Chrome bookmarks file path based on the OS.
Returns the absolute path string.
"""
home = Path.home()
if sys.platform.startswith("win"):
# Windows
base = home / "AppData" / "Local" / "Google" / "Chrome" / "User Data" / "Default"
elif sys.platform.startswith("darwin"):
# macOS
base = home / "Library" / "Application Support" / "Google" / "Chrome" / "Default"
else:
# Linux
base = home / ".config" / "google-chrome" / "Default"
bookmark_file = base / "Bookmarks"
if not bookmark_file.exists():
raise FileNotFoundError(f"Cannot find Chrome Bookmarks file at: {bookmark_file}")
return str(bookmark_file)
def find_folder_by_name(node, target_name):
"""
Recursively searches for a folder with the given name.
"""
if not isinstance(node, dict):
return None
if node.get("type") == "folder" and node.get("name") == target_name:
return node
# Search in children
for child in node.get("children", []):
found = find_folder_by_name(child, target_name)
if found:
return found
return None
def extract_bookmarks_from_folder(node, bookmark_list):
"""
Recursively extracts all bookmarks from a folder node.
"""
if not isinstance(node, dict):
return
if node.get("type") == "url":
# Individual bookmark
bookmark_data = {
"title": node.get("name", ""),
"url": node.get("url", ""),
"date_added": node.get("date_added", ""),
"date_modified": node.get("date_modified", ""),
"id": node.get("id", ""),
}
bookmark_list.append(bookmark_data)
elif node.get("type") == "folder":
# Folder, process children
children = node.get("children", [])
for child in children:
extract_bookmarks_from_folder(child, bookmark_list)
def get_cache_file_path():
"""Returns the path for the bookmark cache file."""
# Create data folder in the root repository
data_dir = Path("data")
data_dir.mkdir(exist_ok=True)
return str(data_dir / "ai_bookmarks_cache.json")
def load_cache():
"""Loads the bookmark cache from JSON file."""
cache_file = get_cache_file_path()
if os.path.exists(cache_file):
try:
with open(cache_file, "r", encoding="utf-8") as f:
return json.load(f)
except Exception as e:
print(f"Error loading cache: {e}")
return {"bookmarks": [], "last_updated": None}
def save_cache(cache_data):
"""Saves the bookmark cache to JSON file."""
cache_file = get_cache_file_path()
try:
with open(cache_file, "w", encoding="utf-8") as f:
json.dump(cache_data, f, indent=2, ensure_ascii=False)
return True
except Exception as e:
print(f"Error saving cache: {e}")
return False
@tool
def update_ai_bookmarks_cache() -> Dict[str, Any]:
"""
Extracts bookmarks from Chrome's 'AI ressources' folder and saves them to the data/ai_bookmarks_cache.json file.
This creates a local cache that avoids direct interaction with Chrome's raw JSON file for subsequent operations.
Returns:
Dictionary with update status and bookmark count.
"""
try:
# Find Chrome bookmarks file
bookmarks_file = find_chrome_bookmarks_file()
# Load Chrome bookmarks
with open(bookmarks_file, "r", encoding="utf-8") as f:
data = json.load(f)
# Find the 'AI ressources' folder
ai_folder = None
roots = data.get("roots", {})
for key in ("bookmark_bar", "other", "synced"):
if key in roots:
ai_folder = find_folder_by_name(roots[key], "AI ressources")
if ai_folder:
break
if not ai_folder:
return {"status": "error", "message": "AI ressources folder not found in bookmarks"}
# Extract bookmarks from AI ressources folder
bookmarks = []
extract_bookmarks_from_folder(ai_folder, bookmarks)
# Create cache data with metadata
cache_data = {
"bookmarks": bookmarks,
"last_updated": datetime.now().isoformat(),
"folder_name": "AI ressources",
"total_count": len(bookmarks),
}
# Save to cache
if save_cache(cache_data):
return {
"status": "success",
"message": f"Successfully updated cache with {len(bookmarks)} bookmarks",
"count": len(bookmarks),
}
else:
return {"status": "error", "message": "Failed to save cache"}
except Exception as e:
return {"status": "error", "message": f"Error updating cache: {str(e)}"}
@tool
def get_latest_ai_bookmarks(n: int = 10) -> List[Dict[str, Any]]:
"""
Gets the n latest bookmarks from the AI ressources cache.
Args:
n: Number of latest bookmarks to return (default: 10)
Returns:
List of the latest bookmarks with metadata.
"""
cache = load_cache()
bookmarks = cache.get("bookmarks", [])
if not bookmarks:
return []
# Sort by date_added (newest first) if available
try:
sorted_bookmarks = sorted(bookmarks, key=lambda x: int(x.get("date_added", "0")), reverse=True)
except (ValueError, TypeError):
# If sorting fails, return as is
sorted_bookmarks = bookmarks
return sorted_bookmarks[:n]
@tool
def search_ai_bookmarks(query: str) -> List[Dict[str, Any]]:
"""
Search AI ressources bookmarks for entries matching a query.
Args:
query: Search term to find in bookmark titles or URLs.
Returns:
List of matching bookmarks.
"""
cache = load_cache()
bookmarks = cache.get("bookmarks", [])
if not bookmarks:
return []
query_lower = query.lower()
matching_bookmarks = []
for bookmark in bookmarks:
title = bookmark.get("title", "").lower()
url = bookmark.get("url", "").lower()
if query_lower in title or query_lower in url:
matching_bookmarks.append(bookmark)
return matching_bookmarks
@tool
def get_bookmark_statistics() -> Dict[str, Any]:
"""
Gets statistics about the AI ressources bookmarks cache.
Returns:
Dictionary with various statistics about the bookmarks.
"""
cache = load_cache()
bookmarks = cache.get("bookmarks", [])
if not bookmarks:
return {"total_count": 0, "last_updated": None}
# Calculate statistics
total_count = len(bookmarks)
domains = {}
for bookmark in bookmarks:
url = bookmark.get("url", "")
try:
from urllib.parse import urlparse
domain = urlparse(url).netloc
domains[domain] = domains.get(domain, 0) + 1
except (ValueError, AttributeError):
pass
# Get top domains
top_domains = sorted(domains.items(), key=lambda x: x[1], reverse=True)[:5]
return {
"total_count": total_count,
"last_updated": cache.get("last_updated"),
"top_domains": top_domains,
"unique_domains": len(domains),
}
@tool
def get_all_ai_bookmarks() -> List[Dict[str, Any]]:
"""
Gets all bookmarks from the AI ressources cache.
Returns:
List of all cached bookmarks.
"""
cache = load_cache()
return cache.get("bookmarks", [])
@tool
def filter_bookmarks_by_domain(domain: str) -> List[Dict[str, Any]]:
"""
Filters AI ressources bookmarks by domain.
Args:
domain: Domain name to filter by (e.g., 'github.com')
Returns:
List of bookmarks from the specified domain.
"""
cache = load_cache()
bookmarks = cache.get("bookmarks", [])
if not bookmarks:
return []
domain_lower = domain.lower()
filtered_bookmarks = []
for bookmark in bookmarks:
url = bookmark.get("url", "")
try:
from urllib.parse import urlparse
bookmark_domain = urlparse(url).netloc.lower()
if domain_lower in bookmark_domain:
filtered_bookmarks.append(bookmark)
except (ValueError, AttributeError):
pass
return filtered_bookmarks
@tool
def get_cache_info() -> Dict[str, Any]:
"""
Gets information about the bookmark cache file.
Returns:
Dictionary with cache file information.
"""
cache_file = get_cache_file_path()
cache = load_cache()
info = {
"cache_file_path": cache_file,
"cache_exists": os.path.exists(cache_file),
"last_updated": cache.get("last_updated"),
"bookmark_count": len(cache.get("bookmarks", [])),
"folder_name": cache.get("folder_name", "Unknown"),
}
if os.path.exists(cache_file):
stat = os.stat(cache_file)
info["file_size_bytes"] = stat.st_size
info["file_modified"] = datetime.fromtimestamp(stat.st_mtime).isoformat()
return info
# Instantiate the Bookmarks CodeAgent with enhanced tools
bookmarks_agent = CodeAgent(
model=InferenceClientModel(
provider="nebius",
token=os.environ["HF_TOKEN"],
),
tools=[
update_ai_bookmarks_cache,
get_latest_ai_bookmarks,
search_ai_bookmarks,
get_bookmark_statistics,
get_all_ai_bookmarks,
filter_bookmarks_by_domain,
get_cache_info,
],
name="bookmarks_agent",
description="Specialized agent for Chrome bookmarks operations, focusing on AI ressources folder. Extracts bookmarks from Chrome and caches them in data/ai_bookmarks_cache.json to avoid direct interaction with Chrome's raw JSON. Provides search, filtering, statistics, and cache management for AI-related bookmarks.",
max_steps=10,
additional_authorized_imports=["json", "datetime", "urllib.parse", "pathlib"],
# Reduce verbosity
stream_outputs=False,
max_print_outputs_length=300,
)