Spaces:
Sleeping
Sleeping
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 | |
| 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)}"} | |
| 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] | |
| 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 | |
| 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), | |
| } | |
| 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", []) | |
| 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 | |
| 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, | |
| ) | |