"""RSS and article tools.""" from __future__ import annotations import logging from fabledassistant.services.tools._registry import tool logger = logging.getLogger(__name__) @tool( name="get_rss_items", description="Get recent items from the user's RSS feeds (news, blogs, Reddit, podcasts). Returns titles, URLs, and summaries of recent posts.", parameters={ "limit": {"type": "integer", "description": "Number of items to return (default 15, max 50)"}, "category": {"type": "string", "description": "Filter by feed category (e.g. 'news', 'tech'). Omit for all."}, }, read_only=True, briefing=True, ) async def get_rss_items_tool(*, user_id, arguments, **_ctx): from fabledassistant.services.rss import get_recent_items limit = min(int(arguments.get("limit", 15)), 50) items = await get_recent_items(user_id, limit=limit) return {"data": {"items": items, "count": len(items)}} @tool( name="add_rss_feed", description="Add an RSS/Atom feed. Use when user asks to subscribe to or track a feed, blog, subreddit, or podcast.", parameters={ "url": {"type": "string", "description": "The RSS/Atom feed URL to add."}, "category": {"type": "string", "description": "Optional category label (e.g. 'news', 'tech', 'reddit'). Omit if unsure."}, }, required=["url"], ) async def add_rss_feed_tool(*, user_id, arguments, **_ctx): import asyncio as _asyncio from sqlalchemy import select as _select from fabledassistant.models import async_session as _async_session from fabledassistant.models.rss_feed import RssFeed from fabledassistant.services.rss import fetch_and_cache_feed url = str(arguments.get("url", "")).strip() if not url: return {"error": "url is required"} category = arguments.get("category") or None async with _async_session() as session: existing = await session.execute( _select(RssFeed).where(RssFeed.user_id == user_id, RssFeed.url == url) ) if existing.scalars().first(): return {"error": "Feed already added", "url": url} feed = RssFeed(user_id=user_id, url=url, title="", category=category) session.add(feed) await session.commit() await session.refresh(feed) feed_id = feed.id _asyncio.create_task(fetch_and_cache_feed(feed_id, url)) return {"data": {"id": feed_id, "url": url, "message": "Feed added and fetching started."}} @tool( name="read_article", description=( "Fetch and read the full text of a web page or article from a URL. " "Use when the user shares a URL and wants you to read it, " "or to get the full content of a linked page. " "Do NOT use search_web for URLs — use this tool instead." ), parameters={ "url": {"type": "string", "description": "The URL to fetch and read"}, }, required=["url"], read_only=True, briefing=True, ) async def read_article_tool(*, user_id, arguments, **_ctx): from fabledassistant.services.rss import _fetch_full_article url = arguments.get("url", "").strip() if not url: return {"success": False, "error": "No URL provided"} content = await _fetch_full_article(url) if not content: return {"success": False, "error": f"Could not fetch article content from {url}"} _TOOL_CONTENT_CAP = 40_000 truncated = len(content) > _TOOL_CONTENT_CAP return { "success": True, "type": "article_content", "url": url, "content": content[:_TOOL_CONTENT_CAP], "truncated": truncated, }